diff --git a/.gradle/6.1.1/fileChanges/last-build.bin b/.gradle/6.1.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/6.1.1/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/6.1.1/fileHashes/fileHashes.lock b/.gradle/6.1.1/fileHashes/fileHashes.lock deleted file mode 100644 index 5f6add4..0000000 Binary files a/.gradle/6.1.1/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/6.1.1/gc.properties b/.gradle/6.1.1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index d090d3d..0000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 95852b7..0000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Fri May 21 10:49:19 IST 2021 -gradle.version=6.1.1 diff --git a/.gradle/checksums/checksums.lock b/.gradle/checksums/checksums.lock deleted file mode 100644 index 1ba7bf7..0000000 Binary files a/.gradle/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/FloatingModMenu.iml b/FloatingModMenu.iml index 4897a34..02b353d 100644 --- a/FloatingModMenu.iml +++ b/FloatingModMenu.iml @@ -1,19 +1,19 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LGL-Il2Cpp-Menu.iml b/LGL-Il2Cpp-Menu.iml index e3ff86f..215f14e 100644 --- a/LGL-Il2Cpp-Menu.iml +++ b/LGL-Il2Cpp-Menu.iml @@ -1,19 +1,19 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/app.iml b/app/app.iml index 69aa1de..924931f 100644 --- a/app/app.iml +++ b/app/app.iml @@ -1,108 +1,108 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b8e28d..caf00ab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,36 +1,36 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 28 - defaultConfig { - applicationId "uk.lgl.modmenu" - minSdkVersion 17 - targetSdkVersion 28 - versionCode 1 - versionName "1.2" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' - } - signingConfig signingConfigs.debug - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - debug { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - externalNativeBuild { - ndkBuild { - path file('src/main/jni/Android.mk') - } - } - ndkVersion = '21.0.6113669' - dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - } +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "uk.lgl.modmenu" + minSdkVersion 17 + targetSdkVersion 28 + versionCode 1 + versionName "1.2" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + ndk { + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' + } + signingConfig signingConfigs.debug + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + debug { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + ndkBuild { + path file('src/main/jni/Android.mk') + } + } + ndkVersion = '21.0.6113669' + dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + } } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 16f0335..64722df 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,22 +1,22 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile -dontobfuscate \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9a0f190..eb89acc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,32 +1,32 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/uk/lgl/modmenu/FloatingModMenuService.java b/app/src/main/java/uk/lgl/modmenu/FloatingModMenuService.java index 757eca0..61fad60 100644 --- a/app/src/main/java/uk/lgl/modmenu/FloatingModMenuService.java +++ b/app/src/main/java/uk/lgl/modmenu/FloatingModMenuService.java @@ -1,3 +1,938 @@ -package uk.lgl.modmenu +/* + * Credit: + * + * Octowolve - Mod menu: https://github.com/z3r0Sec/Substrate-Template-With-Mod-Menu + * And hooking: https://github.com/z3r0Sec/Substrate-Hooking-Example + * VanHoevenTR A.K.A Nixi: https://github.com/LGLTeam/VanHoevenTR_Android_Mod_Menu + * MrIkso - Mod menu: https://github.com/MrIkso/FloatingModMenu + * Rprop - https://github.com/Rprop/And64InlineHook + * MJx0 A.K.A Ruit - KittyMemory: https://github.com/MJx0/KittyMemory + * */ +package uk.lgl.modmenu; +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.AlertDialog; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.GradientDrawable; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.Typeface; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; + +import android.text.Html; +import android.text.InputFilter; +import android.text.InputType; +import android.text.method.DigitsKeyListener; +import android.util.Base64; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.SeekBar; +import android.widget.Spinner; +import android.widget.Switch; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.File; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +import static uk.lgl.modmenu.StaticActivity.cacheDir; +import android.net.ConnectivityManager; +import java.net.HttpURLConnection; +import java.io.BufferedReader; +import java.io.InputStream; +import java.net.URL; +import android.os.AsyncTask; +import android.annotation.SuppressLint; +import java.io.InputStreamReader; +import java.util.Random; +import java.io.FileOutputStream; + +public class FloatingModMenuService extends Service { + private MediaPlayer FXPlayer; + public View mFloatingView; + private Button close; + private Button kill; + private LinearLayout mButtonPanel; + public RelativeLayout mCollapsed; + public LinearLayout mExpanded; + private RelativeLayout mRootContainer; + public WindowManager mWindowManager; + public WindowManager.LayoutParams params; + private LinearLayout patches; + private FrameLayout rootFrame; + private ImageView startimage; + private LinearLayout view1; + private LinearLayout view2; + + private AlertDialog alert; + private EditText edittextvalue; + + private static final String TAG = "Mod Menu"; + + //initialize methods from the native library + public static native String Toast(); + + private native String Icon(); + + private native String Title(); + + private native String Heading(); + + private native boolean EnableSounds(); + + private native int IconSize(); + + public native void Changes(int feature, int value); + + private native String[] getFeatureListttttttttt(); + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + //Override our Start Command so the Service doesnt try to recreate itself when the App is closed + public int onStartCommand(Intent intent, int i, int i2) { + return Service.START_NOT_STICKY; + } + + //When this Class is called the code in this function will be executed + @Override + public void onCreate() { + super.onCreate(); + //A little message for the user when he opens the app + //Toast.makeText(this, Toast(), Toast.LENGTH_LONG).show(); + //Init Lib + + // When you change the lib name, change also on Android.mk file + // Both must have same name + System.loadLibrary("AnaGaby"); + + + initFloating(); + CreateMenuList(); + initAlertDiag(); + final Handler handler = new Handler(); + handler.post(new Runnable() { + public void run() { + handler.postDelayed(this, 1000); + } + }); + } + + + + //Here we write the code for our Menu + + private void initFloating() { + rootFrame = new FrameLayout(getBaseContext()); // Global markup + mRootContainer = new RelativeLayout(getBaseContext()); // Markup on which two markups of the icon and the menu itself will be placed + mCollapsed = new RelativeLayout(getBaseContext()); // Markup of the icon (when the menu is minimized) + mExpanded = new LinearLayout(getBaseContext()); // Menu markup (when the menu is expanded) + view1 = new LinearLayout(getBaseContext()); + patches = new LinearLayout(getBaseContext()); + view2 = new LinearLayout(getBaseContext()); + mButtonPanel = new LinearLayout(getBaseContext()); // Layout of option buttons (when the menu is expanded) + + RelativeLayout relativeLayout = new RelativeLayout(this); + relativeLayout.setLayoutParams(new RelativeLayout.LayoutParams(-2, -1)); + relativeLayout.setPadding(3, 0, 3, 3); + relativeLayout.setVerticalGravity(16); + + kill = new Button(this); + kill.setBackgroundColor(Color.parseColor("#00000000")); + kill.setText("[ ocultar ]"); + + kill.setTextColor(Color.parseColor("WHITE")); + android.graphics.drawable.GradientDrawable CCDIFDH = new android.graphics.drawable.GradientDrawable(); + int CCDIFDHADD[] = new int[]{ Color.argb(255,155,15,15), Color.argb(255,0,15,255) }; + CCDIFDH.setColors(CCDIFDHADD); + CCDIFDH.setOrientation(android.graphics.drawable.GradientDrawable.Orientation.LEFT_RIGHT); + CCDIFDH.setCornerRadius(5); + CCDIFDH.setStroke(5, Color.argb(255,15,15,15)); + kill.setBackground(CCDIFDH); + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(-2, -2); + layoutParams.addRule(11); + + this.close = new Button(this); + this.close.setBackgroundColor(Color.parseColor("#00000000")); + this.close.setText("[ close ]"); + + this.close.setTextColor(Color.WHITE); + close.getBackground().setAlpha(250); + this.close.setLayoutParams(layoutParams); + android.graphics.drawable.GradientDrawable BDFEHGB = new android.graphics.drawable.GradientDrawable(); + int BDFEHGBADD[] = new int[]{ Color.argb(255,155,15,15), Color.argb(255,0,15,255) }; + BDFEHGB.setColors(BDFEHGBADD); + BDFEHGB.setOrientation(android.graphics.drawable.GradientDrawable.Orientation.LEFT_RIGHT); + BDFEHGB.setCornerRadius(5); + BDFEHGB.setStroke(5, Color.argb(255,15,15,15)); + close.setBackground(BDFEHGB); + relativeLayout.addView(kill); + relativeLayout.addView(close); + + rootFrame.setLayoutParams(new FrameLayout.LayoutParams(-1, -1)); + mRootContainer.setLayoutParams(new FrameLayout.LayoutParams(-2, -2)); + mCollapsed.setLayoutParams(new RelativeLayout.LayoutParams(-2, -2)); + mCollapsed.setVisibility(View.VISIBLE); + startimage = new ImageView(getBaseContext()); + startimage.setLayoutParams(new RelativeLayout.LayoutParams(-2, -2)); + int applyDimension = (int) TypedValue.applyDimension(1, (float) IconSize(), getResources().getDisplayMetrics()); + startimage.getLayoutParams().height = applyDimension; + startimage.getLayoutParams().width = applyDimension; + startimage.requestLayout(); + startimage.setScaleType(ImageView.ScaleType.FIT_XY); + byte[] decode = Base64.decode(Icon(), 0); + startimage.setImageBitmap(BitmapFactory.decodeByteArray(decode, 0, decode.length)); + ((ViewGroup.MarginLayoutParams) startimage.getLayoutParams()).topMargin = convertDipToPixels(10); + + mExpanded.setVisibility(View.GONE); + mExpanded.setBackgroundColor(Color.parseColor("BLACK")); + mExpanded.setAlpha(0.95f); + mExpanded.setGravity(17); + mExpanded.setOrientation(LinearLayout.VERTICAL); + mExpanded.setPadding(0, 0, 0, 0); + android.graphics.drawable.GradientDrawable JCFADDB = new android.graphics.drawable.GradientDrawable(); + int JCFADDBADD[] = new int[]{ Color.argb(255,230,15,0), Color.argb(255,0,0,140) }; + JCFADDB.setColors(JCFADDBADD); + + JCFADDB.setOrientation(android.graphics.drawable.GradientDrawable.Orientation.LEFT_RIGHT); + JCFADDB.setCornerRadius(26); + mExpanded.setBackground(JCFADDB); + if(Build.VERSION.SDK_INT >= 21) { mExpanded.setElevation(100f); } + + //Auto size. To set size manually, change the width and height example 500, 500 + mExpanded.setLayoutParams(new LinearLayout.LayoutParams(dp(230), dp(330))); + + ScrollView scrollView = new ScrollView(getBaseContext()); + scrollView.setLayoutParams(new LinearLayout.LayoutParams(-1, dp(205))); + + view1.setLayoutParams(new LinearLayout.LayoutParams(-1, 5)); + view1.setBackgroundColor(Color.parseColor("#000000")); + patches.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)); + patches.setOrientation(LinearLayout.VERTICAL); + view2.setLayoutParams(new LinearLayout.LayoutParams(-1, 5)); + view2.setBackgroundColor(Color.parseColor("#000000")); + view2.setPadding(0, 0, 0, 10); + mButtonPanel.setLayoutParams(new LinearLayout.LayoutParams(-2, -2)); + + //Title text + TextView textView = new TextView(getBaseContext()); + textView.setText(Title()); + textView.setTextColor(Color.parseColor("#ff00ddff")); + textView.setTypeface(Typeface.DEFAULT_BOLD); + textView.setTextSize(20.0f); + textView.setPadding(0, 10, 0, 5); + LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(-2, -2); + layoutParams2.gravity = 17; + //textView.setLayoutParams(layoutParams2); + + //Heading text + TextView textView2 = new TextView(getBaseContext()); + textView2.setText(Html.fromHtml(Heading())); + textView2.setTextColor(Color.parseColor("#ff00ddff")); + textView2.setTypeface(Typeface.DEFAULT_BOLD); + textView2.setTextSize(14.0f); + textView2.setPadding(10, 5, 10, 10); + + LinearLayout.LayoutParams layoutParams3 = new LinearLayout.LayoutParams(-2, -2); + layoutParams3.gravity = 17; + textView.setLayoutParams(layoutParams2); + textView2.setLayoutParams(layoutParams3); + new LinearLayout.LayoutParams(-1, dp(25)).topMargin = dp(2); + rootFrame.addView(mRootContainer); + mRootContainer.addView(mCollapsed); + mRootContainer.addView(mExpanded); + + mCollapsed.addView(startimage); + + mExpanded.addView(textView); + mExpanded.addView(textView2); + mExpanded.addView(view1); + mExpanded.addView(scrollView); + scrollView.addView(patches); + mExpanded.addView(view2); + mExpanded.addView(relativeLayout); + mFloatingView = rootFrame; + if (Build.VERSION.SDK_INT >= 26) { + params = new WindowManager.LayoutParams(-2, -2, 2038, 8, -3); + } else { + params = new WindowManager.LayoutParams(-2, -2, 2002, 8, -3); + } + WindowManager.LayoutParams layoutParams4 = params; + layoutParams4.gravity = 51; + layoutParams4.x = 0; + layoutParams4.y = 100; + mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); + mWindowManager.addView(mFloatingView, params); + + RelativeLayout relativeLayout2 = mCollapsed; + LinearLayout linearLayout = mExpanded; + mFloatingView.setOnTouchListener(onTouchListener()); + startimage.setOnTouchListener(onTouchListener()); + initMenuButton(relativeLayout2, linearLayout); + + } + + private View.OnTouchListener onTouchListener() { + return new View.OnTouchListener() { + final View collapsedView = mCollapsed; + final View expandedView = mExpanded; + private float initialTouchX; + private float initialTouchY; + private int initialX; + private int initialY; + + public boolean onTouch(View view, MotionEvent motionEvent) { + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + initialX = params.x; + initialY = params.y; + initialTouchX = motionEvent.getRawX(); + initialTouchY = motionEvent.getRawY(); + return true; + case MotionEvent.ACTION_UP: + int rawX = (int) (motionEvent.getRawX() - initialTouchX); + int rawY = (int) (motionEvent.getRawY() - initialTouchY); + + //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking. + //So that is click event. + if (rawX < 10 && rawY < 10 && isViewCollapsed()) { + //When user clicks on the image view of the collapsed layout, + //visibility of the collapsed layout will be changed to "View.GONE" + //and expanded view will become visible. + collapsedView.setVisibility(View.GONE); + expandedView.setVisibility(View.VISIBLE); + playSound(Uri.fromFile(new File(cacheDir + "OpenMenu.ogg"))); + Toast.makeText(FloatingModMenuService.this, Html.fromHtml(Toast()), Toast.LENGTH_SHORT).show(); + + } + return true; + case MotionEvent.ACTION_MOVE: + //Calculate the X and Y coordinates of the view. + params.x = initialX + ((int) (motionEvent.getRawX() - initialTouchX)); + params.y = initialY + ((int) (motionEvent.getRawY() - initialTouchY)); + + //Update the layout with new X & Y coordinate + mWindowManager.updateViewLayout(mFloatingView, params); + return true; + default: + return false; + } + } + }; + } + + //Initialize event handlers for buttons, etc. + private void initMenuButton(final View view2, final View view3) { + startimage.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + view2.setVisibility(View.GONE); + view3.setVisibility(View.VISIBLE); + } + }); + kill.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + //FloatingModMenuService.stopSelf(); + // view2.setVisibility(View.VISIBLE) + view2.setVisibility(View.VISIBLE); + view2.setAlpha(0); + view3.setVisibility(View.GONE); + } + }); + close.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + view2.setVisibility(View.VISIBLE); + view2.setAlpha(0.95f); + view3.setVisibility(View.GONE); + //Log.i("LGL", "Close"); + } + }); + } + + private void CreateMenuList() { + String[] listFT = getFeatureListttttttttt(); + for (int i = 0; i < listFT.length; i++) { + final int feature = i; + String str = listFT[i]; + if (str.contains("Toggle_")) { + + addSwitch(str.replace("Toggle_", ""), new InterfaceBool() { + public void OnWrite(boolean z) { + Changes(feature, 0); + } + }); + } else if (str.contains("SeekBar_")) { + String[] split = str.split("_"); + addSeekBar(split[1], Integer.parseInt(split[2]), Integer.parseInt(split[3]), new InterfaceInt() { + public void OnWrite(int i) { + Changes(feature, i); + } + }); + } else if (str.contains("SeekBarSpot_")) { + String[] split = str.split("_"); + addSeekBarSpot(split[1], Integer.parseInt(split[2]), Integer.parseInt(split[3]), new InterfaceInt() { + public void OnWrite(int i) { + Changes(feature, i); + } + }); + } else if (str.contains("Category_")) { + addCategory(str.replace("Category_", "")); + } else if (str.contains("Button_")) { + addButton(str.replace("Button_", ""), new InterfaceBtn() { + public void OnWrite() { + Changes(feature, 0); + } + }); + } else if (str.contains("Spinner_")) { + addSpinner(str.replace("Spinner_", ""), new InterfaceInt() { + @Override + public void OnWrite(int i) { + Changes(feature, i); + } + }); + } else if (str.contains("InputValue_")) { + addTextField(str.replace("InputValue_", ""), feature, new InterfaceInt() { + @Override + public void OnWrite(int i) { + Changes(feature, 0); + } + }); + } + } + } + + private TextView textView2; + private String featureNameExt; + private int featureNum; + private EditTextValue txtValue; + + public class EditTextValue { + private int val; + + public void setValue(int i) { + val = i; + } + + public int getValue() { + return val; + } + } + + private void addTextField(final String featureName, final int feature, final InterfaceInt interInt) { + RelativeLayout relativeLayout2 = new RelativeLayout(this); + relativeLayout2.setLayoutParams(new RelativeLayout.LayoutParams(-2, -1)); + relativeLayout2.setPadding(10, 5, 10, 5); + relativeLayout2.setVerticalGravity(16); + + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(-2, -2); + layoutParams.topMargin = 10; + + final TextView textView = new TextView(this); + textView.setText(Html.fromHtml("" + featureName + ": Not set")); + textView.setTextColor(Color.parseColor("#DEEDF6")); + textView.setLayoutParams(layoutParams); + + final TextView textViewRem = new TextView(this); + textViewRem.setText(""); + + final EditTextValue edittextval = new EditTextValue(); + + RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(-2, -2); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + + Button button2 = new Button(this); + button2.setLayoutParams(layoutParams2); + button2.setBackgroundColor(Color.parseColor("#1C262D")); + button2.setText("SET"); + button2.setTextColor(Color.parseColor("#D5E3EB")); + button2.setGravity(17); + button2.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + alert.show(); + textView2 = textView; + featureNum = feature; + featureNameExt = featureName; + txtValue = edittextval; + + edittextvalue.setText(String.valueOf(edittextval.getValue())); + } + }); + + relativeLayout2.addView(textView); + relativeLayout2.addView(button2); + patches.addView(relativeLayout2); + } + + private void initAlertDiag() { + LinearLayout linearLayout1 = new LinearLayout(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(-1, -1); + linearLayout1.setPadding(10, 5, 0, 5); + linearLayout1.setOrientation(LinearLayout.VERTICAL); + linearLayout1.setGravity(17); + linearLayout1.setLayoutParams(layoutParams); + linearLayout1.setBackgroundColor(Color.parseColor("#FF0000")); + + int i = Build.VERSION.SDK_INT >= 26 ? 2038 : 2002; + LinearLayout linearLayout = new LinearLayout(this); + linearLayout.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)); + linearLayout.setBackgroundColor(Color.parseColor("#14171f")); + linearLayout.setOrientation(LinearLayout.VERTICAL); + FrameLayout frameLayout = new FrameLayout(this); + frameLayout.setLayoutParams(new FrameLayout.LayoutParams(-2, -2)); + frameLayout.addView(linearLayout); + + final TextView textView = new TextView(this); + textView.setText(Html.fromHtml("Tap OK to apply changes. Tap outside to cancel")); + textView.setTextColor(Color.parseColor("#DEEDF6")); + textView.setLayoutParams(layoutParams); + + edittextvalue = new EditText(this); + edittextvalue.setLayoutParams(layoutParams); + edittextvalue.setMaxLines(1); + edittextvalue.setWidth(convertDipToPixels(300)); + edittextvalue.setTextColor(Color.parseColor("#93a6ae")); + edittextvalue.setTextSize(13.0f); + edittextvalue.setHintTextColor(Color.parseColor("#434d52")); + edittextvalue.setInputType(InputType.TYPE_CLASS_NUMBER); + edittextvalue.setKeyListener(DigitsKeyListener.getInstance("0123456789-")); + + InputFilter[] FilterArray = new InputFilter[1]; + FilterArray[0] = new InputFilter.LengthFilter(10); + edittextvalue.setFilters(FilterArray); + + Button button = new Button(this); + button.setBackgroundColor(Color.parseColor("#1C262D")); + button.setTextColor(Color.parseColor("#D5E3EB")); + button.setText("OK"); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Changes(featureNum, Integer.parseInt(edittextvalue.getText().toString())); + txtValue.setValue(Integer.parseInt(edittextvalue.getText().toString())); + textView2.setText(Html.fromHtml("" + featureNameExt + ": " + edittextvalue.getText().toString() + "")); + alert.dismiss(); + playSound(Uri.fromFile(new File(cacheDir + "Select.ogg"))); + //interStr.OnWrite(editText.getText().toString()); + } + }); + + alert = new AlertDialog.Builder(this, 2).create(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Objects.requireNonNull(alert.getWindow()).setType(i); + } + linearLayout1.addView(textView); + linearLayout1.addView(edittextvalue); + linearLayout1.addView(button); + alert.setView(linearLayout1); + } + + private void addSpinner(String feature, final InterfaceInt interInt) { + List list = new LinkedList<>(Arrays.asList(feature.split("_"))); + + LinearLayout linearLayout = new LinearLayout(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(-1, -1); + linearLayout.setPadding(10, 5, 10, 5); + linearLayout.setOrientation(LinearLayout.VERTICAL); + linearLayout.setGravity(17); + linearLayout.setLayoutParams(layoutParams); + linearLayout.setBackgroundColor(Color.parseColor("#00000000")); + + + final TextView textView = new TextView(this); + textView.setText(Html.fromHtml("" + list.get(0) + ": ")); + textView.setTextColor(Color.parseColor("#DEEDF6")); + + // Create another LinearLayout as a workaround to use it as a background + // and to keep the 'down' arrow symbol + // If spinner had the setBackgroundColor set, there would be no arrow symbol + LinearLayout linearLayout2 = new LinearLayout(this); + LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(-1, -1); + layoutParams2.setMargins(10, 2, 10, 5); + linearLayout2.setOrientation(LinearLayout.VERTICAL); + linearLayout2.setGravity(17); + linearLayout2.setBackgroundColor(Color.parseColor("#00000000")); + linearLayout2.setLayoutParams(layoutParams2); + + Spinner spinner = new Spinner(this); + spinner.setPadding(5, 10, 5, 8); + spinner.setLayoutParams(layoutParams2); + spinner.getBackground().setColorFilter(1, PorterDuff.Mode.SRC_ATOP); //trick to show white down arrow color + //Creating the ArrayAdapter instance having the list + list.remove(0); + ArrayAdapter aa = new ArrayAdapter(this, android.R.layout.simple_spinner_item, list); + aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + //Setting the ArrayAdapter data on the Spinner + spinner.setAdapter(aa); + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + ((TextView) parentView.getChildAt(0)).setTextColor(Color.parseColor("#f5f5f5")); + interInt.OnWrite(position); + playSound(Uri.fromFile(new File(cacheDir + "Select.ogg"))); + + } + + @Override + public void onNothingSelected(AdapterView parent) { + playSound(Uri.fromFile(new File(cacheDir + "Select.ogg"))); + } + }); + linearLayout.addView(textView); + linearLayout2.addView(spinner); + patches.addView(linearLayout); + patches.addView(linearLayout2); + } + + private void addCategory(String text) { + TextView textView = new TextView(this); + textView.setBackgroundColor(Color.parseColor("#2F3D4C")); + textView.setText(text); + textView.setGravity(17); + textView.setTextSize(14.0f); + textView.setTextColor(Color.parseColor("#DEEDF6")); + textView.setTypeface(null, Typeface.BOLD); + textView.setPadding(10, 5, 0, 5); + patches.addView(textView); + } + + public void addButton(String feature, final InterfaceBtn interfaceBtn) { + final Button button = new Button(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(-1, -1); + layoutParams.setMargins(7, 5, 7, 5); + button.setLayoutParams(layoutParams); + button.setPadding(10, 5, 10, 5); + button.setTextSize(13.0f); + button.setTextColor(Color.parseColor("#FF0000")); + button.setGravity(17); + + if (feature.contains("OnOff_")) { + feature = feature.replace("OnOff_", ""); + button.setText(feature + " OFF"); + button.setBackgroundColor(Color.parseColor("#FF0000")); + GradientDrawable px = new GradientDrawable(); + px.setShape(GradientDrawable.RECTANGLE); + px.setCornerRadius(8); + px.setGradientType(GradientDrawable.LINEAR_GRADIENT); + px.setStroke(2, (Color.parseColor("#FF0000"))); + px.setColor(Color.parseColor("#FF0000")); + button.setBackground(px); + final String feature2 = feature; + button.setOnClickListener(new View.OnClickListener() { + private boolean isActive = true; + + public void onClick(View v) { + interfaceBtn.OnWrite(); + if (isActive) { + button.setText(feature2 + " ON"); + button.setBackgroundColor(Color.parseColor("#00BFFF")); + GradientDrawable bt = new GradientDrawable(); + bt.setShape(GradientDrawable.RECTANGLE); + bt.setCornerRadius(8); + bt.setGradientType(GradientDrawable.LINEAR_GRADIENT); + bt.setStroke(2, (Color.parseColor("#00BFFF"))); + bt.setColor(Color.parseColor("#00BFFF")); + button.setBackground(bt); + isActive = false; + return; + } + button.setText(feature2 + " OFF"); + button.setBackgroundColor(Color.parseColor("#FF0000")); + GradientDrawable px = new GradientDrawable(); + px.setShape(GradientDrawable.RECTANGLE); + px.setCornerRadius(8); + px.setGradientType(GradientDrawable.LINEAR_GRADIENT); + px.setStroke(2, (Color.parseColor("#00BFFF"))); + px.setColor(Color.parseColor("#FF0000")); + button.setBackground(px); + isActive = true; + } + }); + } else { + button.setText(feature); + button.setBackgroundColor(Color.parseColor("#000000")); + final String feature2 = feature; + button.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + interfaceBtn.OnWrite(); + } + }); + } + patches.addView(button); + } + + private void addSwitch(String str, final InterfaceBool sw) { + final Switch switchR = new Switch(this); + switchR.setBackgroundColor(Color.parseColor("#00000000")); + switchR.setText(Html.fromHtml("" + str + "")); + switchR.setTextColor(Color.parseColor("#ff00ddff")); + switchR.setPadding(10, 5, 0, 5); + switchR.getTrackDrawable().setColorFilter(Color.parseColor("WHITE"), PorterDuff.Mode.SRC_IN); + switchR.getThumbDrawable().setColorFilter(Color.parseColor("RED"), PorterDuff.Mode.SRC_IN); + + + + switchR.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton compoundButton, boolean z) { + if (z) { + switchR.setBackgroundColor(Color.parseColor("#00000000")); + switchR.getTrackDrawable().setColorFilter(Color.parseColor("WHITE"), PorterDuff.Mode.SRC_IN); + switchR.getThumbDrawable().setColorFilter(Color.parseColor("GREEN"), PorterDuff.Mode.SRC_IN); + + } else { + switchR.setBackgroundColor(Color.parseColor("#00000000")); + switchR.getTrackDrawable().setColorFilter(Color.parseColor("WHITE"), PorterDuff.Mode.SRC_IN); + switchR.getThumbDrawable().setColorFilter(Color.parseColor("RED"), PorterDuff.Mode.SRC_IN); + + } + if (z) { + + } else { + + } + sw.OnWrite(z); + } + }); + patches.addView(switchR); + } + + + private void addSeekBar(final String feature, final int prog, int max, final InterfaceInt interInt) { + LinearLayout linearLayout = new LinearLayout(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(-1, -1); + linearLayout.setPadding(10, 5, 0, 5); + linearLayout.setOrientation(LinearLayout.VERTICAL); + linearLayout.setGravity(17); + linearLayout.setLayoutParams(layoutParams); + linearLayout.setBackgroundColor(Color.parseColor("#00000000")); + final TextView textView = new TextView(this); + textView.setText(Html.fromHtml("" + feature + ": " + prog + "")); + textView.setTextColor(Color.parseColor("#ff00ddff")); + textView.setTypeface((Typeface) null, 1); + + SeekBar seekBar = new SeekBar(this); + seekBar.setPadding(25,10,35,10); + seekBar.getProgressDrawable().setColorFilter(Color.parseColor("#00FF00"), PorterDuff.Mode.SRC_IN); + seekBar.getThumb().setColorFilter(Color.parseColor("#00FFFF"), PorterDuff.Mode.SRC_IN); + seekBar.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)); + seekBar.setMax(max); + + seekBar.setProgress(prog); + + final TextView textView2 = textView; + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + public void onStartTrackingTouch(SeekBar seekBar) { + } + + public void onStopTrackingTouch(SeekBar seekBar) { + } + + int l; + + public void onProgressChanged(SeekBar seekBar, int i, boolean z) { + if (l < i) { + + } else { + + } + l = i; + + if (i < prog) { + seekBar.setProgress(prog); + interInt.OnWrite(prog); + TextView textView = textView2; + textView.setText(Html.fromHtml("" + feature + ": " + prog + "")); + return; + } + interInt.OnWrite(i); + textView.setText(Html.fromHtml("" + feature + ": " + i + "")); + } + }); + + linearLayout.addView(textView); + linearLayout.addView(seekBar); + patches.addView(linearLayout); + } + + private void addSeekBarSpot(final String feature, final int prog, int max, final InterfaceInt interInt) { + LinearLayout linearLayout = new LinearLayout(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(-1, -1); + linearLayout.setPadding(10, 5, 0, 5); + linearLayout.setOrientation(LinearLayout.VERTICAL); + linearLayout.setGravity(17); + linearLayout.setLayoutParams(layoutParams); + final TextView textView = new TextView(this); + textView.setText(Html.fromHtml("" + feature + ": " + "Desativado" + "")); + textView.setTextColor(Color.parseColor("#ff00ddff")); + textView.setTypeface((Typeface) null, 1); + SeekBar seekBar = new SeekBar(this); + seekBar.setPadding(25,10,35,10); + seekBar.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)); + seekBar.getProgressDrawable().setColorFilter(Color.parseColor("#00FF00"), PorterDuff.Mode.SRC_IN); + seekBar.getThumb().setColorFilter(Color.parseColor("#00FFFF"), PorterDuff.Mode.SRC_IN); + seekBar.setMax(max); + seekBar.setProgress(prog); + + final TextView textView2 = textView; + final SeekBar seekBar2 = seekBar; + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + public void onStartTrackingTouch(SeekBar seekBar2) { + } + + public void onStopTrackingTouch(SeekBar seekBar2) { + } + + int l; + + public void onProgressChanged(SeekBar seekBar2, int i, boolean z) { + if (i == 0) { + seekBar2.setProgress(i); + interInt.OnWrite(i); + TextView textView = textView2; + textView.setText(Html.fromHtml("" + feature + ": " + "Desativado" + "")); + } else if (i == 1) { + seekBar2.setProgress(i); + interInt.OnWrite(i); + TextView textView = textView2; + textView.setText(Html.fromHtml("" + feature + ": " + "Cabeça" + "")); + } else if (i == 2) { + seekBar2.setProgress(i); + interInt.OnWrite(i); + TextView textView = textView2; + textView.setText(Html.fromHtml("" + feature + ": " + "Corpo" + "")); + } else if (i == 3) { + seekBar2.setProgress(i); + interInt.OnWrite(i); + TextView textView = textView2; + textView.setText(Html.fromHtml("" + feature + ": " + "Pé" + "")); + } + interInt.OnWrite(i); + } + }); + + linearLayout.addView(textView); + linearLayout.addView(seekBar); + patches.addView(linearLayout); + } + + boolean delayed; + + public void playSound(Uri uri) { + if (EnableSounds()) { + if (!delayed) { + delayed = true; + if (FXPlayer != null) { + FXPlayer.stop(); + FXPlayer.release(); + } + FXPlayer = MediaPlayer.create(this, uri); + if (FXPlayer != null) + //Volume reduced so sounds are not too loud + FXPlayer.setVolume(0.5f, 0.5f); + FXPlayer.start(); + + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + delayed = false; + } + }, 100); + } + } + } + + public boolean isViewCollapsed() { + return mFloatingView == null || mCollapsed.getVisibility() == View.VISIBLE; + } + + //For our image a little converter + private int convertDipToPixels(int i) { + return (int) ((((float) i) * getResources().getDisplayMetrics().density) + 0.5f); + } + + private int dp(int i) { + return (int) TypedValue.applyDimension(1, (float) i, getResources().getDisplayMetrics()); + } + + //Destroy our View + public void onDestroy() { + super.onDestroy(); + + if (view2 != null) { + this.mWindowManager.removeView(view2); + } + View view = mFloatingView; + if (view != null) { + mWindowManager.removeView(view); + } + } + + // checking if any network connection / internet available + + // calling our AsyncTask Function that will do thee thing on fetching data from out host file + + // this is the checking one, this will draw our menu if it's license still valid or active + + //Check if we are still in the game. If now our Menu and Menu button will dissapear + private boolean isNotInGame() { + RunningAppProcessInfo runningAppProcessInfo = new RunningAppProcessInfo(); + ActivityManager.getMyMemoryState(runningAppProcessInfo); + return runningAppProcessInfo.importance != 100; + } + + //Same as above so it wont crash in the background and therefore use alot of Battery life + + + private interface InterfaceBtn { + void OnWrite(); + } + + private interface InterfaceInt { + void OnWrite(int i); + } + + private interface InterfaceBool { + void OnWrite(boolean z); + } + + private interface InterfaceStr { + void OnWrite(String s); + } +} diff --git a/app/src/main/java/uk/lgl/modmenu/MainActivity.java b/app/src/main/java/uk/lgl/modmenu/MainActivity.java index e69de29..60d38b9 100644 --- a/app/src/main/java/uk/lgl/modmenu/MainActivity.java +++ b/app/src/main/java/uk/lgl/modmenu/MainActivity.java @@ -0,0 +1,16 @@ +package uk.lgl.modmenu; + +import android.app.Activity; +import android.os.Bundle; + +import uk.lgl.modmenu.R; + +public class MainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + StaticActivity.Start(this); + } +} diff --git a/app/src/main/java/uk/lgl/modmenu/Sounds.java b/app/src/main/java/uk/lgl/modmenu/Sounds.java index e69de29..18c8485 100644 --- a/app/src/main/java/uk/lgl/modmenu/Sounds.java +++ b/app/src/main/java/uk/lgl/modmenu/Sounds.java @@ -0,0 +1,51 @@ +/* + * Credit: + * + * Google - Android UI sounds + * Material.io - https://material.io/design/sound/sound-resources.html# + * + * */ + +package uk.lgl.modmenu; + +public class Sounds { + + //Use https://www.base64encode.org/ to encode your files to base64 + + //Note to myself xd: Click View -> Active Editor -> Soft warp + + //system/media/audio/ui/NFCFailure.ogg + public static String Back() { + return ""; + } + + //system/media/audio/ui/NFCInitiated.ogg + public static String OpenMenu() { + return "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VgAgdoUDwE6EDasjnBSNBRYashIACAcAAIxBiDHoJJRSSoUQY9BJSKW1GCuEGINQSkqttZg85xyEUlpqLcbkOecgpNRajDEm10JIKaWWYouxuBZCKim11mKsyRiVUmotthhr7cWolEpLMcYYazDG5tRajDHWWosxOrcSS4wxxlqEEcbFFmOstdcijBGyxdJarbUGY4yxubXYas25GCOMri21VmvNBQCYPDgAQCXYOMNK0lnhaHChISsBgNwAAAIhpRhjzDnnnHMOQgipUow55xyEEEIIoZRSUqUYc845CCGEUEIppaSMMeYchBBCCKWUUkppKWXMOQghhFBKKaWU0lLrnHMQQgillFJKKSWl1DnnIIRQSimllFJKSi2EEEIooZRSSimllJRSSiGEUEoppZRSSimppZRCCKWUUkoppZRSUkophRBCKaWUUkoppaSUWiullFJKKaWUUkpJLbWUUiillFJKKaWUklpKKaVSSimllFJKKSWl1FJKpZRSSimllFJKS6mllEoppZRSSimllJRSSimlVEoppZRSSikppdRaSimllEoppZRSWmsppZZSKqWUUkoppbTUWmsttZRKKaWUUkpprbWUUkoplVJKKaWUUgAA0IEDAECAEZUWYqcZVx6BIwoZJqBCQ1YCAGQAAAyjlFJJLUWCIqUYpJZCJRVzUFKKKHMOUqypQs4g5iSVijGElINUMgeVUsxBCiFlTCkGrZUYOsaYo5hqKqFjDAAAAEEAAIGQCQQKoMBABgAcICRIAQCFBYYOESJAjAID4+LSBgAgCJEZIhGxGCQmVANFxXQAsLjAkA8AGRobaRcX0GWAC7q460AIQQhCEIsDKCABByfc8MQbnnCDE3SKSh0IAAAAAIADADwAACQbQERENHMcHR4fICEiIyQlJicoAgAAAADgBgAfAABJChAREc0cR4fHB0iIyAhJickJSgAAIIAAAAAAAAggAAEBAQAAAACAAAAAAAEBT2dnUwAELZ8AAAAAAABzsqlwAgAAAKUXno1bOP82/zb/Kv8g/z3/OP9Y/2r/dP9m/5P/rf+L/5P/mkdUTEv/mf+w/3r/pv9b/77/ef9nPD49PDw9PUVA/2L/Tv9a/2//XP9k/1r/W/9L/5L/YP+a/0H/kf93JQwh6wJOnEayCDmJu7bgX+ZHLSpTIf2/WsUHuvG5J8cuWkvurT62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"; + } + + //system/media/audio/ui/NFCTransferComplete.ogg + public static String Select() { + return "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"; + } + + //Material sounds - state-change_confirm-up.ogg + public static String On() { + return "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VgAgdoUDwE6EDasjnBSNBRYashIACAcAAIxBiDHoJJRSSoUQY9BJSKW1GCuEGINQSkqttZg85xyEUlpqLcbkOecgpNRajDEm10JIKaWWYouxuBZCKim11mKsyRiVUmotthhr7cWolEpLMcYYazDG5tRajDHWWosxOrcSS4wxxlqEEcbFFmOstdcijBGyxdJarbUGY4yxubXYas25GCOMri21VmvNBQCYPDgAQCXYOMNK0lnhaHChISsBgNwAAAIhpRhjzDnnnHMOQgipUow55xyEEEIIoZRSUqUYc845CCGEUEIppaSMMeYchBBCCKWUUkppKWXMOQghhFBKKaWU0lLrnHMQQgillFJKKSWl1DnnIIRQSimllFJKSi2EEEIooZRSSimllJRSSiGEUEoppZRSSimppZRCCKWUUkoppZRSUkophRBCKaWUUkoppaSUWiullFJKKaWUUkpJLbWUUiillFJKKaWUklpKKaVSSimllFJKKSWl1FJKpZRSSimllFJKS6mllEoppZRSSimllJRSSimlVEoppZRSSikppdRaSimllEoppZRSWmsppZZSKqWUUkoppbTUWmsttZRKKaWUUkpprbWUUkoplVJKKaWUUgAA0IEDAECAEZUWYqcZVx6BIwoZJqBCQ1YCAGQAAAyjlFJJLUWCIqUYpJZCJRVzUFKKKHMOUqypQs4g5iSVijGElINUMgeVUsxBCiFlTCkGrZUYOsaYo5hqKqFjDAAAAEEAAIGQCQQKoMBABgAcICRIAQCFBYYOESJAjAID4+LSBgAgCJEZIhGxGCQmVANFxXQAsLjAkA8AGRobaRcX0GWAC7q460AIQQhCEIsDKCABByfc8MQbnnCDE3SKSh0IAAAAAEADADwAACQbQERENHMcHR4fICEiIyQlJicoAgAAAABgBgAfAABJChAREc0cR4fHB0iIyAhJickJSgAAIIAAAAAAAAggAAEBAQAAAACAAAAAAAEBT2dnUwAEQIkAAAAAAAAuvakYAgAAAOSFo8FBAQEBOz+vdnNwkDw8Ozo7PTo+Oj6/joGDhYGOiYmGh/84/1b/Vv9W/1b/Vv9W/1b/Vv9W/1b/Vv9W/1b/Vv9W/1YAAABEjk0x+nhoIse+G873yB63n4prKDRYnMyNVtNcXS0FwRIEwg7X1TYoCoIgCKQo3q/PjDKwop//fP77ALS+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"; + } + + //Material sounds - state-change_confirm-down.ogg + public static String Off() { + return "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"; + } + + //Material sounds - navigation_forward-selection-minimal.ogg + public static String SliderIncrease() { + return "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"; + } + + //Material sounds - navigation_forward-selection-minimal.ogg + public static String SliderDecrease() { + return "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"; + } +} diff --git a/app/src/main/java/uk/lgl/modmenu/StaticActivity.java b/app/src/main/java/uk/lgl/modmenu/StaticActivity.java index e69de29..72378a1 100644 --- a/app/src/main/java/uk/lgl/modmenu/StaticActivity.java +++ b/app/src/main/java/uk/lgl/modmenu/StaticActivity.java @@ -0,0 +1,99 @@ +package uk.lgl.modmenu; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.net.Uri; +import android.os.Build; +import android.os.Handler; +import android.os.Process; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.Html; +import android.util.Base64; +import android.util.Log; +import android.widget.Toast; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class StaticActivity { + + private static final String TAG = "Mod Menu"; + public static String cacheDir; + + public static void Start(final Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) { + context.startActivity(new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION", + Uri.parse("package:" + context.getPackageName()))); + Process.killProcess(Process.myPid()); + } else { + + // Delay starting service to prevent function pointer issue + // Arcording to Guided Hacking: + // https://guidedhacking.com/threads/android-function-pointers-hooking-template-tutorial.14771/#post-90490 + // The il2cpp lib sometimes don't loaded first which caused crash when declaring the function pointer. + // Instead splitting the function pointer, delay the service. The Il2Cpp will load first + // before the service start + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + context.startService(new Intent(context, FloatingModMenuService.class)); + } + }, 1000); + } + + cacheDir = context.getCacheDir().getPath() + "/"; + + writeToFile("OpenMenu.ogg", Sounds.OpenMenu()); + writeToFile("Back.ogg", Sounds.Back()); + writeToFile("Select.ogg", Sounds.Select()); + writeToFile("SliderIncrease.ogg", Sounds.SliderIncrease()); + writeToFile("SliderDecrease.ogg", Sounds.SliderDecrease()); + writeToFile("On.ogg", Sounds.On()); + writeToFile("Off.ogg", Sounds.Off()); + + /* AssetManager assets = context.getAssets(); + String str2 = cacheDir + "/Slider-Switch.ogg"; + try { + copyFile(assets.open("Slider-Switch.ogg"), new FileOutputStream(str2)); + } catch (IOException e) { + e.printStackTrace(); + }*/ + } + + private static void writeToFile(String name, String base64) { + File file = new File(cacheDir + name); + try { + if (!file.exists()) { + file.createNewFile(); + } + FileOutputStream fos = new FileOutputStream(file); + byte[] decode = Base64.decode(base64, 0); + fos.write(decode); + fos.close(); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } + } + + /*private static void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException { + byte[] bArr = new byte[1024]; + while (true) { + int read = inputStream.read(bArr); + if (read != -1) { + outputStream.write(bArr, 0, read); + } else { + return; + } + } + }*/ +} diff --git a/app/src/main/jni/src/And64InlineHook/And64InlineHook.cpp b/app/src/main/jni/src/And64InlineHook/And64InlineHook.cpp index 18d4836..34bf9a0 100644 --- a/app/src/main/jni/src/And64InlineHook/And64InlineHook.cpp +++ b/app/src/main/jni/src/And64InlineHook/And64InlineHook.cpp @@ -1,4 +1,31 @@ - +/* + * @date : 2018/04/18 + * @author : Rprop (r_prop@outlook.com) + * https://github.com/Rprop/And64InlineHook + */ +/* + MIT License + + Copyright (c) 2018 Rprop (r_prop@outlook.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ #define __STDC_FORMAT_MACROS #include diff --git a/app/src/main/jni/src/And64InlineHook/And64InlineHook.hpp b/app/src/main/jni/src/And64InlineHook/And64InlineHook.hpp index 668ada8..c76c852 100644 --- a/app/src/main/jni/src/And64InlineHook/And64InlineHook.hpp +++ b/app/src/main/jni/src/And64InlineHook/And64InlineHook.hpp @@ -1,3 +1,31 @@ +/* + * @date : 2018/04/18 + * @author : Rprop (r_prop@outlook.com) + * https://github.com/Rprop/And64InlineHook + */ +/* + MIT License + + Copyright (c) 2018 Rprop (r_prop@outlook.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ #pragma once #define A64_MAX_BACKUPS 256 diff --git a/app/src/main/jni/src/And64InlineHook/LICENSE b/app/src/main/jni/src/And64InlineHook/LICENSE new file mode 100644 index 0000000..75a6020 --- /dev/null +++ b/app/src/main/jni/src/And64InlineHook/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 RLib + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/src/main/jni/src/And64InlineHook/README.md b/app/src/main/jni/src/And64InlineHook/README.md index 9019040..44f651d 100644 --- a/app/src/main/jni/src/And64InlineHook/README.md +++ b/app/src/main/jni/src/And64InlineHook/README.md @@ -1,3 +1,7 @@ -# And64LineHook +# And64InlineHook +Lightweight ARMv8-A(ARM64, AArch64, Little-Endian) Inline Hook Library for Android C/C++ + +# References +[Arm Compiler armasm User Guide](http://infocenter.arm.com/help/topic/com.arm.doc.100069_0610_00_en/pge1427898258836.html) +[Procedure Call Standard for the Arm® 64-bit Architecture (AArch64)](https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst) -- Lightweight ARMv8-A(ARM64, AArch64, Little-Endian) Inline Hook Library for Android C/C++ \ No newline at end of file diff --git a/app/src/main/jni/src/Global.h b/app/src/main/jni/src/Global.h index b11aa80..e8d4587 100644 --- a/app/src/main/jni/src/Global.h +++ b/app/src/main/jni/src/Global.h @@ -1,98 +1,98 @@ -#ifndef ANDROID_MOD_MENU_GLOBAL_H -#define ANDROID_MOD_MENU_GLOBAL_H - -struct { - - uintptr_t MainCameraTransform = 0x50; // public Transform MainCameraTransform; // 0x50 (1.54.2) - uintptr_t Dictionary = 0x44; // protected Dictionary b]T^b|Q; // 0x44 (1.54.2) - uintptr_t HeadTF = 0x1A4; // protected Transform s^€uN~t; // 0x1A4 (1.54.2) - uintptr_t HipTF = 0x1A8; // protected Transform sqdz[Xb; // 0x1A8 (1.54.2) - uintptr_t HandTF = 0x1A0; // protected Transform {vps^€o; // 0x1A0 (1.54.2) - uintptr_t EyeTF = 0x1AC; // protected Transform mB]ByDk; // 0x1AC (1.54.2) - uintptr_t ToeTF = 0x1B4; // protected Transform hFLM]|b; // 0x1B4 (1.54.2) - uintptr_t RightShoulder = 0x1D4; // protected Transform OjNRJt; // 0x1D4 (1.54.2) - uintptr_t LeftShoulder = 0x1D0; // protected Transform mcbhyOv; // 0x1D0 (1.54.2) - uintptr_t IsClientBot = 0xC4; // public bool IsClientBot; // 0xC4 (1.54.2) - uintptr_t U3DStr = 0x228396C; // 1.54.2 - uintptr_t U3DStrConcat = 0x228210C; // 1.54.2 - uintptr_t Component_GetTransform = 0x2729DC4; // public Transform get_transform() 1.54.2 - uintptr_t GetCameraTrackableEntityTransfrom = 0xA37568; // public Transform GetCameraTrackableEntityTransfrom 1.54.2 - uintptr_t Transform_INTERNAL_GetPosition = 0x2D3638C; // private void INTERNAL_get_position 1.54.2 - uintptr_t Transform_INTERNAL_SetPosition = 0x2D3644C; // private void INTERNAL_set_position 1.54.2 - uintptr_t GetForward = 0x2D36A70; // public Vector3 get_forward 1.54.2 - uintptr_t get_isAlive = 0xA37650; // public bool IsCameraTrackableEntityAlive 1.54.2 - uintptr_t GetPhysXPose = 0xA5B828; // public xl^LGZc GetPhysXPose 1.54.2 - uintptr_t IsFiring = 0xA6DBFC; // public bool IsFiring 1.54.2 - uintptr_t IsCrouching = 0xA5B85C; // public bool IsCrouching 1.54.2 - uintptr_t get_IsSighting = 0xAA1964; // public bool get_IsSighting 1.54.2 - uintptr_t get_IsReallyDead = 0xA521B0; // public bool get_IsReallyDead 1.54.2 - uintptr_t get_isLocalPlayer = 0xA54178; // public bool IsLocalPlayer 1.54.2 - uintptr_t get_isLocalTeam = 0xA59E10; // public virtual bool IsLocalTeammate 1.54.2 - uintptr_t get_isVisible = 0xA53758; // public override bool IsVisible 1.54.2 - uintptr_t set_aim = 0xA51D64; // public void SetAimRotation 1.54.2 - uintptr_t Camera_main_fov = 0x2722EFC; // public float get_fieldOfView 1.54.2 - uintptr_t get_imo = 0xA3DB18; // public FPnavhE GetActiveWeapon 1.54.2 - uintptr_t set_esp = 0xF482E0; // public void €fxZep 1.54.2 - uintptr_t GetAttackableCenterWS = 0xA50948; // public override Vector3 GetAttackableCenterWS 1.54.2 - uintptr_t GetCharacterControllerTopPosition = 0xA8DACC; // public virtual Vector3 GetCharacterControllerTopPosition 1.54.2 - uintptr_t get_NickName = 0xA35B00; // public string get_NickName 1.54.2 - uintptr_t WorldToScreenPoint = 0x2724AA0; // public Vector3 WorldToScreenPoint 1.54.2 - uintptr_t get_height = 0x2B8B43C; // public static int get_height 1.54.2 - uintptr_t get_width = 0x2B8B3AC; // public static int get_width 1.54.2 - uintptr_t get_deltaTime = 0x2D34E34; // public static float get_fixedDeltaTime 1.54.2 - uintptr_t CurrentUIScene = 0x1FAAFDC; // public static UICOWBaseScene CurrentUIScene 1.54.2 - uintptr_t Curent_Match = 0x1FBEDF8; // public static Th|G[l[ CurrentMatch 1.54.2 - uintptr_t Current_Local_Player = 0x1FBF14C; // public static Player CurrentLocalPlayer 1.54.2 - uintptr_t GetLocalPlayerOrObServer = 0x1FC03F0; // public static Player GetLocalPlayerOrObServer 1.54.2 - uintptr_t CurrentLocalSpectator = 0x1FBF5CC; // public static olfyRLw CurrentLocalSpectator() 1.54.2 - uintptr_t Player_Index = 0x1597968; // public Player ]xBnHak(byte WMRg}) { } 1.54.2 - uintptr_t AddTeammateHud = 0x135E088; // public void ShowAssistantText(string playerName, string line) { } 1.54.2 - uintptr_t spof_uid = 0xA50D68; // protected void kjkIWoo(ulong ‚aKRQdu) { } 1.54.2 - uintptr_t spof_nick = 0xA50E58; // protected void rhTOWJa(string ‚aKRQdu) { } 1.54.2 - uintptr_t ShowDynamicPopupMessage = 0x134BBA0; // public void ShowDynamicPopupMessage(string message, float duration = 5) { } 1.54.2 - uintptr_t ShowPopupMessage = 0x134BD28; // public void ShowPopupMessage(string message, float duration = 2) { } 1.54.2 - uintptr_t GetLocalPlayer = 0x28FC52C; // private static Player GetLocalPlayer() { } 1.54.2 - uintptr_t GetCharacterHeight = 0xA62404; // public float GetCharacterHeight() 1.54.2 - uintptr_t set_height = 0x27286D0; // public void set_height(float value) { } 1.54.2 - uintptr_t get_CharacterController = 0xA511B4; // public CharacterController get_CharacterController() 1.54.2 - uintptr_t IsUserControlChanged = 0xA5B740; // public bool IsUserControlChanged() 1.54.2 - uintptr_t set_invitee_nickname = 0x2A799E0; // public void set_invitee_nickname(string value) { } 1.54.2 - uintptr_t Raycast = 0x2A7367C; // public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask) { } 1.54.2 - uintptr_t get_MyFollowCamera = 0xA516E8; // public FollowCamera get_MyFollowCamera() 1.54.2 - uintptr_t IsSameTeam = 0x21E49E8; // protected override void OnUIInit() 1.54.2 - uintptr_t AttackableEntity_GetIsDead = 0x1D57DA4; // public bool get_IsDead() 1.54.2 - uintptr_t AttackableEntity_IsVisible = 0x1D57E9C; // public virtual bool IsVisible() 1.54.2 - uintptr_t Camera_WorldToScreenPoint = 0x2724AA0; // public Vector3 WorldToScreenPoint(Vector3 position)1.54.2 - uintptr_t Camera_main = 0x2725090; // public static Camera get_main 1.54.2 - uintptr_t telamento2 = 0x14BE898; // public int get_CurrentSpectatorCount 1.54.2 - uintptr_t telamentoforce = 0x2D1C0AC; // public uint[] get_spectators() 1.54.2 - uintptr_t noRecoil = 0xACBC84; // public float GetScatterRate 1.54.2 - uintptr_t GetHead = 0xA8D704; // 1.54.2 - uintptr_t GetHipTF = 0xA8D830; // 1.54.2 - uintptr_t CurrentMatch = 0x1FBEDF8; // 1.54.2 - uintptr_t GetLocalPlayer2 = 0x15974D0; // 1.54.2 - uintptr_t getPlayerByIndex = 0x1597968; // 1.54.2 - uintptr_t get_CurHP = 0xA82994; // 1.54.2 - uintptr_t get_PlayerID = 0xA31FAC; // 1.54.2 - uintptr_t get_IsDieing = 0xA515F4; // 1.54.2 - uintptr_t get_IsSkyDiving = 0xA3D93C; // 1.54.2 - uintptr_t get_IsSkyDashing = 0xA52EE8; // 1.54.2 - uintptr_t get_IsParachuting = 0xA3D994; // 1.54.2 - uintptr_t SetAimRotation = 0xA51D64; // 1.54.2 - uintptr_t get_MaxHP = 0x23F3068; // 1.54 - uintptr_t il2cpp_string_new = 0x2E2CE0C; // 1.53.2 no DUMP - uintptr_t String_Concat = 0x22741E0; // 1.54.2 - uintptr_t Screen_get_width = 0x2B8B3AC; // 1.54.2 - uintptr_t Screen_get_height = 0x2B8B43C; // 1.54.2 - uintptr_t get_IsCrouching = 0xA5B85C; // 1.54.2 - uintptr_t get_main = 0x2725090; // 1.54.2 - uintptr_t Component_get_transform = 0x2729DC4; // 1.54.2 - uintptr_t Transform_get_position = 0x2D3638C; // 1.54.2 - uintptr_t IsVisible = 0xA53758; // 1.54.2 - uintptr_t IsLocalPlayer = 0xA54178; // 1.54.2 - uintptr_t Transform_get_forward = 0x2D36A70; // 1.54.2 - uintptr_t IsLocalTeammate = 0xA59E10; // 1.54.2 - -} Global; - -#endif +#ifndef ANDROID_MOD_MENU_GLOBAL_H +#define ANDROID_MOD_MENU_GLOBAL_H + +struct { + + uintptr_t MainCameraTransform = 0x50; // public Transform MainCameraTransform; // 0x50 (1.54.2) + uintptr_t Dictionary = 0x44; // protected Dictionary b]T^b|Q; // 0x44 (1.54.2) + uintptr_t HeadTF = 0x1A4; // protected Transform s^€uN~t; // 0x1A4 (1.54.2) + uintptr_t HipTF = 0x1A8; // protected Transform sqdz[Xb; // 0x1A8 (1.54.2) + uintptr_t HandTF = 0x1A0; // protected Transform {vps^€o; // 0x1A0 (1.54.2) + uintptr_t EyeTF = 0x1AC; // protected Transform mB]ByDk; // 0x1AC (1.54.2) + uintptr_t ToeTF = 0x1B4; // protected Transform hFLM]|b; // 0x1B4 (1.54.2) + uintptr_t RightShoulder = 0x1D4; // protected Transform OjNRJt; // 0x1D4 (1.54.2) + uintptr_t LeftShoulder = 0x1D0; // protected Transform mcbhyOv; // 0x1D0 (1.54.2) + uintptr_t IsClientBot = 0xC4; // public bool IsClientBot; // 0xC4 (1.54.2) + uintptr_t U3DStr = 0x228396C; // 1.54.2 + uintptr_t U3DStrConcat = 0x228210C; // 1.54.2 + uintptr_t Component_GetTransform = 0x2729DC4; // public Transform get_transform() 1.54.2 + uintptr_t GetCameraTrackableEntityTransfrom = 0xA37568; // public Transform GetCameraTrackableEntityTransfrom 1.54.2 + uintptr_t Transform_INTERNAL_GetPosition = 0x2D3638C; // private void INTERNAL_get_position 1.54.2 + uintptr_t Transform_INTERNAL_SetPosition = 0x2D3644C; // private void INTERNAL_set_position 1.54.2 + uintptr_t GetForward = 0x2D36A70; // public Vector3 get_forward 1.54.2 + uintptr_t get_isAlive = 0xA37650; // public bool IsCameraTrackableEntityAlive 1.54.2 + uintptr_t GetPhysXPose = 0xA5B828; // public xl^LGZc GetPhysXPose 1.54.2 + uintptr_t IsFiring = 0xA6DBFC; // public bool IsFiring 1.54.2 + uintptr_t IsCrouching = 0xA5B85C; // public bool IsCrouching 1.54.2 + uintptr_t get_IsSighting = 0xAA1964; // public bool get_IsSighting 1.54.2 + uintptr_t get_IsReallyDead = 0xA521B0; // public bool get_IsReallyDead 1.54.2 + uintptr_t get_isLocalPlayer = 0xA54178; // public bool IsLocalPlayer 1.54.2 + uintptr_t get_isLocalTeam = 0xA59E10; // public virtual bool IsLocalTeammate 1.54.2 + uintptr_t get_isVisible = 0xA53758; // public override bool IsVisible 1.54.2 + uintptr_t set_aim = 0xA51D64; // public void SetAimRotation 1.54.2 + uintptr_t Camera_main_fov = 0x2722EFC; // public float get_fieldOfView 1.54.2 + uintptr_t get_imo = 0xA3DB18; // public FPnavhE GetActiveWeapon 1.54.2 + uintptr_t set_esp = 0xF482E0; // public void €fxZep 1.54.2 + uintptr_t GetAttackableCenterWS = 0xA50948; // public override Vector3 GetAttackableCenterWS 1.54.2 + uintptr_t GetCharacterControllerTopPosition = 0xA8DACC; // public virtual Vector3 GetCharacterControllerTopPosition 1.54.2 + uintptr_t get_NickName = 0xA35B00; // public string get_NickName 1.54.2 + uintptr_t WorldToScreenPoint = 0x2724AA0; // public Vector3 WorldToScreenPoint 1.54.2 + uintptr_t get_height = 0x2B8B43C; // public static int get_height 1.54.2 + uintptr_t get_width = 0x2B8B3AC; // public static int get_width 1.54.2 + uintptr_t get_deltaTime = 0x2D34E34; // public static float get_fixedDeltaTime 1.54.2 + uintptr_t CurrentUIScene = 0x1FAAFDC; // public static UICOWBaseScene CurrentUIScene 1.54.2 + uintptr_t Curent_Match = 0x1FBEDF8; // public static Th|G[l[ CurrentMatch 1.54.2 + uintptr_t Current_Local_Player = 0x1FBF14C; // public static Player CurrentLocalPlayer 1.54.2 + uintptr_t GetLocalPlayerOrObServer = 0x1FC03F0; // public static Player GetLocalPlayerOrObServer 1.54.2 + uintptr_t CurrentLocalSpectator = 0x1FBF5CC; // public static olfyRLw CurrentLocalSpectator() 1.54.2 + uintptr_t Player_Index = 0x1597968; // public Player ]xBnHak(byte WMRg}) { } 1.54.2 + uintptr_t AddTeammateHud = 0x135E088; // public void ShowAssistantText(string playerName, string line) { } 1.54.2 + uintptr_t spof_uid = 0xA50D68; // protected void kjkIWoo(ulong ‚aKRQdu) { } 1.54.2 + uintptr_t spof_nick = 0xA50E58; // protected void rhTOWJa(string ‚aKRQdu) { } 1.54.2 + uintptr_t ShowDynamicPopupMessage = 0x134BBA0; // public void ShowDynamicPopupMessage(string message, float duration = 5) { } 1.54.2 + uintptr_t ShowPopupMessage = 0x134BD28; // public void ShowPopupMessage(string message, float duration = 2) { } 1.54.2 + uintptr_t GetLocalPlayer = 0x28FC52C; // private static Player GetLocalPlayer() { } 1.54.2 + uintptr_t GetCharacterHeight = 0xA62404; // public float GetCharacterHeight() 1.54.2 + uintptr_t set_height = 0x27286D0; // public void set_height(float value) { } 1.54.2 + uintptr_t get_CharacterController = 0xA511B4; // public CharacterController get_CharacterController() 1.54.2 + uintptr_t IsUserControlChanged = 0xA5B740; // public bool IsUserControlChanged() 1.54.2 + uintptr_t set_invitee_nickname = 0x2A799E0; // public void set_invitee_nickname(string value) { } 1.54.2 + uintptr_t Raycast = 0x2A7367C; // public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask) { } 1.54.2 + uintptr_t get_MyFollowCamera = 0xA516E8; // public FollowCamera get_MyFollowCamera() 1.54.2 + uintptr_t IsSameTeam = 0x21E49E8; // protected override void OnUIInit() 1.54.2 + uintptr_t AttackableEntity_GetIsDead = 0x1D57DA4; // public bool get_IsDead() 1.54.2 + uintptr_t AttackableEntity_IsVisible = 0x1D57E9C; // public virtual bool IsVisible() 1.54.2 + uintptr_t Camera_WorldToScreenPoint = 0x2724AA0; // public Vector3 WorldToScreenPoint(Vector3 position)1.54.2 + uintptr_t Camera_main = 0x2725090; // public static Camera get_main 1.54.2 + uintptr_t telamento2 = 0x14BE898; // public int get_CurrentSpectatorCount 1.54.2 + uintptr_t telamentoforce = 0x2D1C0AC; // public uint[] get_spectators() 1.54.2 + uintptr_t noRecoil = 0xACBC84; // public float GetScatterRate 1.54.2 + uintptr_t GetHead = 0xA8D704; // 1.54.2 + uintptr_t GetHipTF = 0xA8D830; // 1.54.2 + uintptr_t CurrentMatch = 0x1FBEDF8; // 1.54.2 + uintptr_t GetLocalPlayer2 = 0x15974D0; // 1.54.2 + uintptr_t getPlayerByIndex = 0x1597968; // 1.54.2 + uintptr_t get_CurHP = 0xA82994; // 1.54.2 + uintptr_t get_PlayerID = 0xA31FAC; // 1.54.2 + uintptr_t get_IsDieing = 0xA515F4; // 1.54.2 + uintptr_t get_IsSkyDiving = 0xA3D93C; // 1.54.2 + uintptr_t get_IsSkyDashing = 0xA52EE8; // 1.54.2 + uintptr_t get_IsParachuting = 0xA3D994; // 1.54.2 + uintptr_t SetAimRotation = 0xA51D64; // 1.54.2 + uintptr_t get_MaxHP = 0x23F3068; // 1.54 + uintptr_t il2cpp_string_new = 0x2E2CE0C; // 1.53.2 no DUMP + uintptr_t String_Concat = 0x22741E0; // 1.54.2 + uintptr_t Screen_get_width = 0x2B8B3AC; // 1.54.2 + uintptr_t Screen_get_height = 0x2B8B43C; // 1.54.2 + uintptr_t get_IsCrouching = 0xA5B85C; // 1.54.2 + uintptr_t get_main = 0x2725090; // 1.54.2 + uintptr_t Component_get_transform = 0x2729DC4; // 1.54.2 + uintptr_t Transform_get_position = 0x2D3638C; // 1.54.2 + uintptr_t IsVisible = 0xA53758; // 1.54.2 + uintptr_t IsLocalPlayer = 0xA54178; // 1.54.2 + uintptr_t Transform_get_forward = 0x2D36A70; // 1.54.2 + uintptr_t IsLocalTeammate = 0xA59E10; // 1.54.2 + +} Global; + +#endif diff --git a/app/src/main/jni/src/Includes/Logger.h b/app/src/main/jni/src/Includes/Logger.h index f3549ec..83a0580 100644 --- a/app/src/main/jni/src/Includes/Logger.h +++ b/app/src/main/jni/src/Includes/Logger.h @@ -1,20 +1,20 @@ -#ifndef MRZ_LOGGER_H -#define MRZ_LOGGER_H -#include - -enum daLogType { - daDEBUG = 3, - daERROR = 6, - daINFO = 4, - daWARN = 5 -}; - -//Change this to another Log Tag if ya want. IN the batch script I provide you change the log tag then too -#define TAG "MRZ" - -#define LOGD(...) ((void)__android_log_print(daDEBUG, TAG, __VA_ARGS__)) -#define LOGE(...) ((void)__android_log_print(daERROR, TAG, __VA_ARGS__)) -#define LOGI(...) ((void)__android_log_print(daINFO, TAG, __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(daWARN, TAG, __VA_ARGS__)) - +#ifndef MRZ_LOGGER_H +#define MRZ_LOGGER_H +#include + +enum daLogType { + daDEBUG = 3, + daERROR = 6, + daINFO = 4, + daWARN = 5 +}; + +//Change this to another Log Tag if ya want. IN the batch script I provide you change the log tag then too +#define TAG "MRZ" + +#define LOGD(...) ((void)__android_log_print(daDEBUG, TAG, __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(daERROR, TAG, __VA_ARGS__)) +#define LOGI(...) ((void)__android_log_print(daINFO, TAG, __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(daWARN, TAG, __VA_ARGS__)) + #endif //DAWN_LOGGER_H \ No newline at end of file diff --git a/app/src/main/jni/src/Includes/README.md b/app/src/main/jni/src/Includes/README.md new file mode 100644 index 0000000..389a960 --- /dev/null +++ b/app/src/main/jni/src/Includes/README.md @@ -0,0 +1,6 @@ +# Features + +### Affile Cipher + + +### Linear Congruential Random Generating \ No newline at end of file diff --git a/app/src/main/jni/src/Includes/Utils.h b/app/src/main/jni/src/Includes/Utils.h index 0bb6b50..ab13c7c 100644 --- a/app/src/main/jni/src/Includes/Utils.h +++ b/app/src/main/jni/src/Includes/Utils.h @@ -1,57 +1,57 @@ -#ifndef UTILS_H -#define UTILS_H - -#include -#include "src/KittyMemory/MemoryPatch.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "src/Unity/Unity.h" - -typedef unsigned long DWORD; - -DWORD libBase = 0; - -const char* libName = "libil2cpp.so"; - -DWORD get_libBase(const char* libName); -DWORD getRealOffset(DWORD address); - -DWORD get_libBase(const char* libName) { - FILE *fp; - DWORD addr = 0; - char filename[32], buffer[1024]; - snprintf(filename, sizeof(filename), "/proc/%d/maps", getpid()); - fp = fopen(filename, "rt"); - if (fp != NULL) { - while (fgets(buffer, sizeof(buffer), fp)) { - if (strstr(buffer, libName)) { - addr = (uintptr_t) strtoul(buffer, NULL, 16); - break; - } - } - fclose(fp); - } - return addr; -} - -DWORD getRealOffset(DWORD address) { - if (libBase == 0) { - libBase = get_libBase(libName); - } - return (libBase + address); -} - +#ifndef UTILS_H +#define UTILS_H + +#include +#include "src/KittyMemory/MemoryPatch.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "src/Unity/Unity.h" + +typedef unsigned long DWORD; + +DWORD libBase = 0; + +const char* libName = "libil2cpp.so"; + +DWORD get_libBase(const char* libName); +DWORD getRealOffset(DWORD address); + +DWORD get_libBase(const char* libName) { + FILE *fp; + DWORD addr = 0; + char filename[32], buffer[1024]; + snprintf(filename, sizeof(filename), "/proc/%d/maps", getpid()); + fp = fopen(filename, "rt"); + if (fp != NULL) { + while (fgets(buffer, sizeof(buffer), fp)) { + if (strstr(buffer, libName)) { + addr = (uintptr_t) strtoul(buffer, NULL, 16); + break; + } + } + fclose(fp); + } + return addr; +} + +DWORD getRealOffset(DWORD address) { + if (libBase == 0) { + libBase = get_libBase(libName); + } + return (libBase + address); +} + #endif \ No newline at end of file diff --git a/app/src/main/jni/src/Includes/obfuscate.h b/app/src/main/jni/src/Includes/obfuscate.h index 71f7964..5c2dc3f 100644 --- a/app/src/main/jni/src/Includes/obfuscate.h +++ b/app/src/main/jni/src/Includes/obfuscate.h @@ -167,3 +167,28 @@ namespace ay return OBFUSCATE_data; \ }() +/* -------------------------------- LICENSE ------------------------------------ + +Public Domain (http://www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------------------------------------------------------------------------- */ diff --git a/app/src/main/jni/src/Substrate/Buffer.hpp b/app/src/main/jni/src/Substrate/Buffer.hpp index c18d663..34d9df3 100644 --- a/app/src/main/jni/src/Substrate/Buffer.hpp +++ b/app/src/main/jni/src/Substrate/Buffer.hpp @@ -1,3 +1,23 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ #ifndef SUBSTRATE_BUFFER_HPP #define SUBSTRATE_BUFFER_HPP diff --git a/app/src/main/jni/src/Substrate/CydiaSubstrate.h b/app/src/main/jni/src/Substrate/CydiaSubstrate.h index 9c60dd3..bb806aa 100644 --- a/app/src/main/jni/src/Substrate/CydiaSubstrate.h +++ b/app/src/main/jni/src/Substrate/CydiaSubstrate.h @@ -1,3 +1,23 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ #ifndef SUBSTRATE_H_ #define SUBSTRATE_H_ diff --git a/app/src/main/jni/src/Substrate/SubstrateARM.hpp b/app/src/main/jni/src/Substrate/SubstrateARM.hpp index a3b8937..02b3028 100644 --- a/app/src/main/jni/src/Substrate/SubstrateARM.hpp +++ b/app/src/main/jni/src/Substrate/SubstrateARM.hpp @@ -1,3 +1,24 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + #ifndef SUBSTRATE_ARM_HPP #define SUBSTRATE_ARM_HPP diff --git a/app/src/main/jni/src/Substrate/SubstrateDebug.cpp b/app/src/main/jni/src/Substrate/SubstrateDebug.cpp index 68f986c..2df6ef4 100644 --- a/app/src/main/jni/src/Substrate/SubstrateDebug.cpp +++ b/app/src/main/jni/src/Substrate/SubstrateDebug.cpp @@ -1,3 +1,24 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + #include "SubstrateHook.h" #include "SubstrateDebug.hpp" diff --git a/app/src/main/jni/src/Substrate/SubstrateDebug.hpp b/app/src/main/jni/src/Substrate/SubstrateDebug.hpp index 1f75953..9c554c8 100644 --- a/app/src/main/jni/src/Substrate/SubstrateDebug.hpp +++ b/app/src/main/jni/src/Substrate/SubstrateDebug.hpp @@ -1,3 +1,23 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ #ifndef SUBSTRATE_DEBUG_HPP #define SUBSTRATE_DEBUG_HPP diff --git a/app/src/main/jni/src/Substrate/SubstrateHook.cpp b/app/src/main/jni/src/Substrate/SubstrateHook.cpp index e69de29..5250bba 100644 --- a/app/src/main/jni/src/Substrate/SubstrateHook.cpp +++ b/app/src/main/jni/src/Substrate/SubstrateHook.cpp @@ -0,0 +1,937 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#define SubstrateInternal +#include "CydiaSubstrate.h" + +#include + +#define _trace() do { \ + MSLog(MSLogLevelNotice, "_trace(%u)", __LINE__); \ +} while (false) + +#if defined(__i386__) || defined(__x86_64__) +#include "hde64.h" +#endif + +#include "SubstrateDebug.hpp" + +#include +#include +#include + +#ifdef __arm__ +/* WebCore (ARM) PC-Relative: +X 1 ldr r*,[pc,r*] != + 2 fldd d*,[pc,#*] +X 5 str r*,[pc,r*] != + 8 flds s*,[pc,#*] + 400 ldr r*,[pc,r*] == + 515 add r*, pc,r* == +X 4790 ldr r*,[pc,#*] */ + +// x=0; while IFS= read -r line; do if [[ ${#line} -ne 0 && $line == +([^\;]): ]]; then x=2; elif [[ $line == ' +'* && $x -ne 0 ]]; then ((--x)); echo "$x${line}"; fi; done WebCore.pc +// grep pc WebCore.pc | cut -c 40- | sed -Ee 's/^ldr *(ip|r[0-9]*),\[pc,\#0x[0-9a-f]*\].*/ ldr r*,[pc,#*]/;s/^add *r[0-9]*,pc,r[0-9]*.*/ add r*, pc,r*/;s/^(st|ld)r *r([0-9]*),\[pc,r([0-9]*)\].*/ \1r r\2,[pc,r\3]/;s/^fld(s|d) *(s|d)[0-9]*,\[pc,#0x[0-9a-f]*].*/fld\1 \2*,[pc,#*]/' | sort | uniq -c | sort -n + +#include "SubstrateARM.hpp" + +#define T$Label(l, r) \ + (((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2)) + +#define T$pop_$r0$ 0xbc01 // pop {r0} +#define T$b(im) /* b im */ \ + (0xde00 | (im & 0xff)) +#define T$blx(rm) /* blx rm */ \ + (0x4780 | (rm << 3)) +#define T$bx(rm) /* bx rm */ \ + (0x4700 | (rm << 3)) +#define T$nop /* nop */ \ + (0x46c0) + +#define T$add_rd_rm(rd, rm) /* add rd, rm */ \ + (0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7)) +#define T$push_r(r) /* push r... */ \ + (0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff)) +#define T$pop_r(r) /* pop r... */ \ + (0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff)) +#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \ + (0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7)) +#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \ + (0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd)) +#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \ + (0x4800 | ((rd) << 8) | ((im) & 0xff)) +#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \ + (0x2000 | ((rn) << 8) | ((im) & 0xff)) +#define T$it$_cd(cd, ms) /* it, cd */ \ + (0xbf00 | ((cd) << 4) | (ms)) +#define T$cbz$_rn_$im(op,rn,im) /* cbz rn, #im */ \ + (0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn)) +#define T$b$_$im(cond,im) /* b #im */ \ + (cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff)) + +#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \ + (0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn)) +#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \ + (((rt) << 12) | abs((int)(im))) + +#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \ + (0xf3ef) +#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \ + (0x8000 | ((rd) << 8)) + +#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (0xf380 | (rn)) +#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (0x8c00) +#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn)) + +static inline bool A$pcrel$r(uint32_t ic) { + return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000; +} + +static inline bool T$32bit$i(uint16_t ic) { + return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000); +} + +static inline bool T$pcrel$cbz(uint16_t ic) { + return (ic & 0xf500) == 0xb100; +} + +static inline bool T$pcrel$b(uint16_t ic) { + return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00; +} + +static inline bool T2$pcrel$b(uint16_t *ic) { + return (ic[0] & 0xf800) == 0xf000 && (((ic[1] & 0xd000) == 0x9000 || (ic[1] & 0xd000) == 0x8000) && (ic[0] & 0x0380) != 0x0380); +} + +static inline bool T$pcrel$bl(uint16_t *ic) { + return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000); +} + +static inline bool T$pcrel$ldr(uint16_t ic) { + return (ic & 0xf800) == 0x4800; +} + +static inline bool T$pcrel$add(uint16_t ic) { + return (ic & 0xff78) == 0x4478; +} + +static inline bool T$pcrel$ldrw(uint16_t ic) { + return (ic & 0xff7f) == 0xf85f; +} + +static size_t MSGetInstructionWidthThumb(void *start) { + uint16_t *thumb(reinterpret_cast(start)); + return T$32bit$i(thumb[0]) ? 4 : 2; +} + +static size_t MSGetInstructionWidthARM(void *start) { + return 4; +} + +extern "C" size_t MSGetInstructionWidth(void *start) { + if ((reinterpret_cast(start) & 0x1) == 0) + return MSGetInstructionWidthARM(start); + else + return MSGetInstructionWidthThumb(reinterpret_cast(reinterpret_cast(start) & ~0x1)); +} + +static size_t SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (symbol == NULL) + return 0; +printf("SubstrateHookFunctionThumb\n"); + uint16_t *area(reinterpret_cast(symbol)); + + unsigned align((reinterpret_cast(area) & 0x2) == 0 ? 0 : 1); + uint16_t *thumb(area + align); + + uint32_t *arm(reinterpret_cast(thumb + 2)); + uint16_t *trail(reinterpret_cast(arm + 2)); + + if ( + (align == 0 || area[0] == T$nop) && + thumb[0] == T$bx(A$pc) && + thumb[1] == T$nop && + arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8) + ) { + if (result != NULL) + *result = reinterpret_cast(arm[1]); + + SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1); + + arm[1] = reinterpret_cast(replace); + + return sizeof(arm[0]); + } + + size_t required((trail - area) * sizeof(uint16_t)); + + size_t used(0); + while (used < required) + used += MSGetInstructionWidthThumb(reinterpret_cast(area) + used); + used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t); + + size_t blank((used - required) / sizeof(uint16_t)); + + uint16_t backup[used / sizeof(uint16_t)]; + memcpy(backup, area, used); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint16_t), 2, name); + } + + if (result != NULL) { + + size_t length(used); + for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) + if (T$pcrel$ldr(backup[offset])) + length += 3 * sizeof(uint16_t); + else if (T$pcrel$b(backup[offset])) + length += 6 * sizeof(uint16_t); + else if (T2$pcrel$b(backup + offset)) { + length += 5 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$bl(backup + offset)) { + length += 5 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$cbz(backup[offset])) { + length += 16 * sizeof(uint16_t); + } else if (T$pcrel$ldrw(backup[offset])) { + length += 4 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$add(backup[offset])) + length += 6 * sizeof(uint16_t); + else if (T$32bit$i(backup[offset])) + ++offset; + + unsigned pad((length & 0x2) == 0 ? 0 : 1); + length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t); + + uint16_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return 0; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return 0; + } + + size_t start(pad), end(length / sizeof(uint16_t)); + uint32_t *trailer(reinterpret_cast(buffer + end)); + for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) { + if (T$pcrel$ldr(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t immediate : 8; + uint16_t rd : 3; + uint16_t : 5; + }; + } bits = {backup[offset+0]}; + + buffer[start+0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+0, end-2) / 4); + buffer[start+1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0); + + // XXX: this code "works", but is "wrong": the mechanism is more complex than this + *--trailer = ((reinterpret_cast(area + offset) + 4) & ~0x2) + bits.immediate * 4; + + start += 2; + end -= 2; + } else if (T$pcrel$b(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t imm8 : 8; + uint16_t cond : 4; + uint16_t /*1101*/ : 4; + }; + } bits = {backup[offset+0]}; + + intptr_t jump(bits.imm8 << 1); + jump |= 1; + jump <<= 23; + jump >>= 23; + + buffer[start+0] = T$b$_$im(bits.cond, (end-6 - (start+0)) * 2 - 4); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + + start += 1; + end -= 6; + } else if (T2$pcrel$b(backup + offset)) { + union { + uint16_t value; + + struct { + uint16_t imm6 : 6; + uint16_t cond : 4; + uint16_t s : 1; + uint16_t : 5; + }; + } bits = {backup[offset+0]}; + + union { + uint16_t value; + + struct { + uint16_t imm11 : 11; + uint16_t j2 : 1; + uint16_t a : 1; + uint16_t j1 : 1; + uint16_t : 2; + }; + } exts = {backup[offset+1]}; + + intptr_t jump(1); + jump |= exts.imm11 << 1; + jump |= bits.imm6 << 12; + + if (exts.a) { + jump |= bits.s << 24; + jump |= (~(bits.s ^ exts.j1) & 0x1) << 23; + jump |= (~(bits.s ^ exts.j2) & 0x1) << 22; + jump |= bits.cond << 18; + jump <<= 7; + jump >>= 7; + } else { + jump |= bits.s << 20; + jump |= exts.j2 << 19; + jump |= exts.j1 << 18; + jump <<= 11; + jump >>= 11; + } + + buffer[start+0] = T$b$_$im(exts.a ? A$al : bits.cond, (end-6 - (start+0)) * 2 - 4); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + + ++offset; + start += 1; + end -= 6; + } else if (T$pcrel$bl(backup + offset)) { + union { + uint16_t value; + + struct { + uint16_t immediate : 10; + uint16_t s : 1; + uint16_t : 5; + }; + } bits = {backup[offset+0]}; + + union { + uint16_t value; + + struct { + uint16_t immediate : 11; + uint16_t j2 : 1; + uint16_t x : 1; + uint16_t j1 : 1; + uint16_t : 2; + }; + } exts = {backup[offset+1]}; + + int32_t jump(0); + jump |= bits.s << 24; + jump |= (~(bits.s ^ exts.j1) & 0x1) << 23; + jump |= (~(bits.s ^ exts.j2) & 0x1) << 22; + jump |= bits.immediate << 12; + jump |= exts.immediate << 1; + jump |= exts.x; + jump <<= 7; + jump >>= 7; + + buffer[start+0] = T$push_r(1 << A$r7); + buffer[start+1] = T$ldr_rd_$pc_im_4$(A$r7, ((end-2 - (start+1)) * 2 - 4 + 2) / 4); + buffer[start+2] = T$mov_rd_rm(A$lr, A$r7); + buffer[start+3] = T$pop_r(1 << A$r7); + buffer[start+4] = T$blx(A$lr); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + + ++offset; + start += 5; + end -= 2; + } else if (T$pcrel$cbz(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t rn : 3; + uint16_t immediate : 5; + uint16_t : 1; + uint16_t i : 1; + uint16_t : 1; + uint16_t op : 1; + uint16_t : 4; + }; + } bits = {backup[offset+0]}; + + intptr_t jump(1); + jump |= bits.i << 6; + jump |= bits.immediate << 1; + + //jump <<= 24; + //jump >>= 24; + + unsigned rn(bits.rn); + unsigned rt(rn == A$r7 ? A$r6 : A$r7); + + buffer[start+0] = T$push_r(1 << rt); + buffer[start+1] = T1$mrs_rd_apsr(rt); + buffer[start+2] = T2$mrs_rd_apsr(rt); + buffer[start+3] = T$cbz$_rn_$im(bits.op, rn, (end-10 - (start+3)) * 2 - 4); + buffer[start+4] = T1$msr_apsr_nzcvqg_rn(rt); + buffer[start+5] = T2$msr_apsr_nzcvqg_rn(rt); + buffer[start+6] = T$pop_r(1 << rt); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + *--trailer = T$nop << 16 | T$pop_r(1 << rt); + *--trailer = T$msr_apsr_nzcvqg_rn(rt); + +#if 0 + if ((start & 0x1) == 0) + buffer[start++] = T$nop; + buffer[start++] = T$bx(A$pc); + buffer[start++] = T$nop; + + uint32_t *arm(reinterpret_cast(buffer + start)); + arm[0] = A$add(A$lr, A$pc, 1); + arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8); +#endif + + start += 7; + end -= 10; + } else if (T$pcrel$ldrw(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t : 7; + uint16_t u : 1; + uint16_t : 8; + }; + } bits = {backup[offset+0]}; + + union { + uint16_t value; + + struct { + uint16_t immediate : 12; + uint16_t rt : 4; + }; + } exts = {backup[offset+1]}; + + buffer[start+0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2)); + buffer[start+1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2)); + + buffer[start+2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0); + buffer[start+3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0); + + // XXX: this code "works", but is "wrong": the mechanism is more complex than this + *--trailer = ((reinterpret_cast(area + offset) + 4) & ~0x2) + (bits.u == 0 ? -exts.immediate : exts.immediate); + + ++offset; + start += 4; + end -= 2; + } else if (T$pcrel$add(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t rd : 3; + uint16_t rm : 3; + uint16_t h2 : 1; + uint16_t h1 : 1; + uint16_t : 8; + }; + } bits = {backup[offset+0]}; + + if (bits.h1) { + MSLog(MSLogLevelError, "MS:Error:pcrel(%u):add (rd > r7)", offset); + goto fail; + } + + unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7); + + buffer[start+0] = T$push_r(1 << rt); + buffer[start+1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd); + buffer[start+2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+2, end-2) / 4); + buffer[start+3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt); + buffer[start+4] = T$pop_r(1 << rt); + *--trailer = reinterpret_cast(area + offset) + 4; + + start += 5; + end -= 2; + } else if (T$32bit$i(backup[offset])) { + buffer[start++] = backup[offset]; + buffer[start++] = backup[++offset]; + } else { + buffer[start++] = backup[offset]; + } + } + + buffer[start++] = T$bx(A$pc); + buffer[start++] = T$nop; + + uint32_t *transfer = reinterpret_cast(buffer + start); + transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + transfer[1] = reinterpret_cast(area + used / sizeof(uint16_t)) + 1; + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + return 0; + } + + *result = reinterpret_cast(buffer + pad) + 1; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHexEx(buffer, length, 2, name); + } + + } + + { + SubstrateHookMemory code(process, area, used); + + if (align != 0) + area[0] = T$nop; + + thumb[0] = T$bx(A$pc); + thumb[1] = T$nop; + + arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + arm[1] = reinterpret_cast(replace); + + for (unsigned offset(0); offset != blank; ++offset) + trail[offset] = T$nop; + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint16_t), 2, name); + } + + return used; +} + +static size_t SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (symbol == NULL) + return 0; +printf("SubstrateHookFunctionARM\n"); + uint32_t *area(reinterpret_cast(symbol)); + uint32_t *arm(area); + + const size_t used(8); + + uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]}; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint32_t), 4, name); + } + + if (result != NULL) { + + if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) { + *result = reinterpret_cast(backup[1]); + + return sizeof(backup[0]); + } + + size_t length(used); + for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset) + if (A$pcrel$r(backup[offset])) { + if ((backup[offset] & 0x02000000) == 0 || (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f)) + length += 2 * sizeof(uint32_t); + else + length += 4 * sizeof(uint32_t); + } + + length += 2 * sizeof(uint32_t); + + uint32_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return 0; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return 0; + } + + size_t start(0), end(length / sizeof(uint32_t)); + uint32_t *trailer(reinterpret_cast(buffer + end)); + for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset) + if (A$pcrel$r(backup[offset])) { + union { + uint32_t value; + + struct { + uint32_t rm : 4; + uint32_t : 1; + uint32_t shift : 2; + uint32_t shiftamount : 5; + uint32_t rd : 4; + uint32_t rn : 4; + uint32_t l : 1; + uint32_t w : 1; + uint32_t b : 1; + uint32_t u : 1; + uint32_t p : 1; + uint32_t mode : 1; + uint32_t type : 2; + uint32_t cond : 4; + }; + } bits = {backup[offset+0]}, copy(bits); + + bool guard; + if (bits.mode == 0 || bits.rd != bits.rm) { + copy.rn = bits.rd; + guard = false; + } else { + copy.rn = bits.rm != A$r0 ? A$r0 : A$r1; + guard = true; + } + + if (guard) + buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn)); + + buffer[start+0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, (end-1 - (start+0)) * 4 - 8); + buffer[start+1] = copy.value; + + start += 2; + + if (guard) + buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn)); + + *--trailer = reinterpret_cast(area + offset) + 8; + end -= 1; + } else + buffer[start++] = backup[offset]; + + buffer[start+0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + buffer[start+1] = reinterpret_cast(area + used / sizeof(uint32_t)); + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + goto fail; + } + + *result = buffer; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHexEx(buffer, length, 4, name); + } + + } + + { + SubstrateHookMemory code(process, symbol, used); + + arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + arm[1] = reinterpret_cast(replace); + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint32_t), 4, name); + } + + return used; +} + +static size_t SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (MSDebug) + MSLog(MSLogLevelNotice, "SubstrateHookFunction(%p, %p, %p, %p)\n", process, symbol, replace, result); + if ((reinterpret_cast(symbol) & 0x1) == 0) + return SubstrateHookFunctionARM(process, symbol, replace, result); + else + return SubstrateHookFunctionThumb(process, reinterpret_cast(reinterpret_cast(symbol) & ~0x1), replace, result); +} +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#include "SubstrateX86.hpp" + +static size_t MSGetInstructionWidthIntel(void *start) { + hde64s decode; + return hde64_disasm(start, &decode); +} + +static void SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (MSDebug) + MSLog(MSLogLevelNotice, "MSHookFunction(%p, %p, %p)\n", symbol, replace, result); + if (symbol == NULL) + return; + + uintptr_t source(reinterpret_cast(symbol)); + uintptr_t target(reinterpret_cast(replace)); + + uint8_t *area(reinterpret_cast(symbol)); + + size_t required(MSSizeOfJump(target, source)); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, 32, name); + } + + size_t used(0); + while (used < required) { + size_t width(MSGetInstructionWidthIntel(area + used)); + if (width == 0) { + MSLog(MSLogLevelError, "MS:Error:MSGetInstructionWidthIntel(%p) == 0", area + used); + return; + } + + used += width; + } + + size_t blank(used - required); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, used + sizeof(uint16_t), name); + } + + uint8_t backup[used]; + memcpy(backup, area, used); + + if (result != NULL) { + + if (backup[0] == 0xe9) { + *result = reinterpret_cast(source + 5 + *reinterpret_cast(backup + 1)); + return; + } + + if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) { + *result = *reinterpret_cast(source + 6 + *reinterpret_cast(backup + 2)); + return; + } + + size_t length(used + MSSizeOfJump(source + used)); + + for (size_t offset(0), width; offset != used; offset += width) { + hde64s decode; + hde64_disasm(backup + offset, &decode); + width = decode.len; + //_assert(width != 0 && offset + width <= used); + +#ifdef __LP64__ + if ((decode.modrm & 0xc7) == 0x05) { + if (decode.opcode == 0x8b) { + void *destiny(area + offset + width + int32_t(decode.disp.disp32)); + uint8_t reg(decode.rex_r << 3 | decode.modrm_reg); + length -= decode.len; + length += MSSizeOfPushPointer(destiny); + length += MSSizeOfPop(reg); + length += MSSizeOfMove64(); + } else { + MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2); + continue; + } + } else +#endif + + if (backup[offset] == 0xe8) { + int32_t relative(*reinterpret_cast(backup + offset + 1)); + void *destiny(area + offset + decode.len + relative); + + if (relative == 0) { + length -= decode.len; + length += MSSizeOfPushPointer(destiny); + } else { + length += MSSizeOfSkip(); + length += MSSizeOfJump(destiny); + } + } else if (backup[offset] == 0xeb) { + length -= decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } else if (backup[offset] == 0xe9) { + length -= decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } else if ( + backup[offset] == 0xe3 || + (backup[offset] & 0xf0) == 0x70 + // XXX: opcode2 & 0xf0 is 0x80? + ) { + length += decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } + } + + uint8_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return; + } + + { + uint8_t *current(buffer); + + for (size_t offset(0), width; offset != used; offset += width) { + hde64s decode; + hde64_disasm(backup + offset, &decode); + width = decode.len; + //_assert(width != 0 && offset + width <= used); + +#ifdef __LP64__ + if ((decode.modrm & 0xc7) == 0x05) { + if (decode.opcode == 0x8b) { + void *destiny(area + offset + width + int32_t(decode.disp.disp32)); + uint8_t reg(decode.rex_r << 3 | decode.modrm_reg); + MSPushPointer(current, destiny); + MSWritePop(current, reg); + MSWriteMove64(current, reg, reg); + } else { + MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2); + goto copy; + } + } else +#endif + + if (backup[offset] == 0xe8) { + int32_t relative(*reinterpret_cast(backup + offset + 1)); + if (relative == 0) + MSPushPointer(current, area + offset + decode.len); + else { + MSWrite(current, 0xe8); + MSWrite(current, MSSizeOfSkip()); + void *destiny(area + offset + decode.len + relative); + MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip())); + MSWriteJump(current, destiny); + } + } else if (backup[offset] == 0xeb) + MSWriteJump(current, area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + else if (backup[offset] == 0xe9) + MSWriteJump(current, area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + else if ( + backup[offset] == 0xe3 || + (backup[offset] & 0xf0) == 0x70 + ) { + MSWrite(current, backup[offset]); + MSWrite(current, 2); + MSWrite(current, 0xeb); + void *destiny(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + MSWrite(current, MSSizeOfJump(destiny, current + 1)); + MSWriteJump(current, destiny); + } else +#ifdef __LP64__ + copy: +#endif + { + MSWrite(current, backup + offset, width); + } + } + + MSWriteJump(current, area + used); + } + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + goto fail; + } + + *result = buffer; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHex(buffer, length, name); + } + + } + + { + SubstrateHookMemory code(process, area, used); + + uint8_t *current(area); + MSWriteJump(current, target); + for (unsigned offset(0); offset != blank; ++offset) + MSWrite(current, 0x90); + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, used + sizeof(uint16_t), name); + } +} +#endif + +_extern void MSHookFunction(void *symbol, void *replace, void **result) { +#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) + SubstrateHookFunction(NULL, symbol, replace, result); +#endif +} + + +#if defined(__APPLE__) && defined(__arm__) +_extern void _Z14MSHookFunctionPvS_PS_(void *symbol, void *replace, void **result) { + return MSHookFunction(symbol, replace, result); +} +#endif diff --git a/app/src/main/jni/src/Substrate/SubstrateHook.h b/app/src/main/jni/src/Substrate/SubstrateHook.h index e69de29..40a0296 100644 --- a/app/src/main/jni/src/Substrate/SubstrateHook.h +++ b/app/src/main/jni/src/Substrate/SubstrateHook.h @@ -0,0 +1,19 @@ +#ifndef __SUBSTRATEHOOK_H__ +#define __SUBSTRATEHOOK_H__ + + +#include + +#define _extern extern "C" __attribute__((__visibility__("default"))) + +#ifdef __cplusplus +extern "C" { +#endif + +void MSHookFunction(void *symbol, void *replace, void **result); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/jni/src/Substrate/SubstrateLog.hpp b/app/src/main/jni/src/Substrate/SubstrateLog.hpp index e69de29..3e57280 100644 --- a/app/src/main/jni/src/Substrate/SubstrateLog.hpp +++ b/app/src/main/jni/src/Substrate/SubstrateLog.hpp @@ -0,0 +1,40 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_LOG_HPP +#define SUBSTRATE_LOG_HPP + +#if 0 +#include + +#define MSLog(level, format, ...) ((void)__android_log_print(level, "NNNN", format, __VA_ARGS__)) + +#define MSLogLevelNotice ANDROID_LOG_INFO +#define MSLogLevelWarning ANDROID_LOG_WARN +#define MSLogLevelError ANDROID_LOG_ERROR + +#else + +#define MSLog(level, format, ...) printf(format, __VA_ARGS__) + +#endif + +#endif//SUBSTRATE_LOG_HPP diff --git a/app/src/main/jni/src/Substrate/SubstratePosixMemory.cpp b/app/src/main/jni/src/Substrate/SubstratePosixMemory.cpp index e69de29..92a1e7b 100644 --- a/app/src/main/jni/src/Substrate/SubstratePosixMemory.cpp +++ b/app/src/main/jni/src/Substrate/SubstratePosixMemory.cpp @@ -0,0 +1,75 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#define SubstrateInternal +#include "CydiaSubstrate.h" +#include "SubstrateLog.hpp" + +#include + +#include +#include +#include + +extern "C" void __clear_cache (void *beg, void *end); + +struct __SubstrateMemory { + void *address_; + size_t width_; + + __SubstrateMemory(void *address, size_t width) : + address_(address), + width_(width) + { + } +}; + +extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) { + if (allocator != NULL) { + MSLog(MSLogLevelError, "MS:Error:allocator != %d", 0); + return NULL; + } + + if (size == 0) + return NULL; + + int page(getpagesize()); + + uintptr_t base(reinterpret_cast(data) / page * page); + size_t width(((reinterpret_cast(data) + size - 1) / page + 1) * page - base); + void *address(reinterpret_cast(base)); + + if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno); + return NULL; + } + + return new __SubstrateMemory(address, width); +} + +extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) { + if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) + MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno); + + __clear_cache(reinterpret_cast(memory->address_), reinterpret_cast(memory->address_) + memory->width_); + + delete memory; +} diff --git a/app/src/main/jni/src/Substrate/SubstrateX86.hpp b/app/src/main/jni/src/Substrate/SubstrateX86.hpp index e69de29..ffe2b06 100644 --- a/app/src/main/jni/src/Substrate/SubstrateX86.hpp +++ b/app/src/main/jni/src/Substrate/SubstrateX86.hpp @@ -0,0 +1,200 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_X86_HPP +#define SUBSTRATE_X86_HPP + +#include "Buffer.hpp" + +#ifdef __LP64__ +static const bool ia32 = false; +#else +static const bool ia32 = true; +#endif + +enum I$r { + I$rax, I$rcx, I$rdx, I$rbx, + I$rsp, I$rbp, I$rsi, I$rdi, + I$r8, I$r9, I$r10, I$r11, + I$r12, I$r13, I$r14, I$r15, +}; + +_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) { + intptr_t offset(target - source); + return int32_t(offset) == offset; +} + +_disused static size_t MSSizeOfSkip() { + return 5; +} + +_disused static size_t MSSizeOfPushPointer(uintptr_t target) { + return uint64_t(target) >> 32 == 0 ? 5 : 13; +} + +_disused static size_t MSSizeOfPushPointer(void *target) { + return MSSizeOfPushPointer(reinterpret_cast(target)); +} + +_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) { + if (ia32 || !blind && MSIs32BitOffset(target, source + 5)) + return MSSizeOfSkip(); + else + return MSSizeOfPushPointer(target) + 1; +} + +_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) { + return MSSizeOfJump(false, target, source); +} + +_disused static size_t MSSizeOfJump(uintptr_t target) { + return MSSizeOfJump(true, target); +} + +_disused static size_t MSSizeOfJump(void *target, void *source) { + return MSSizeOfJump(reinterpret_cast(target), reinterpret_cast(source)); +} + +_disused static size_t MSSizeOfJump(void *target) { + return MSSizeOfJump(reinterpret_cast(target)); +} + +_disused static void MSWriteSkip(uint8_t *¤t, ssize_t size) { + MSWrite(current, 0xe9); + MSWrite(current, size); +} + +_disused static void MSPushPointer(uint8_t *¤t, uintptr_t target) { + MSWrite(current, 0x68); + MSWrite(current, target); + + if (uint32_t high = uint64_t(target) >> 32) { + MSWrite(current, 0xc7); + MSWrite(current, 0x44); + MSWrite(current, 0x24); + MSWrite(current, 0x04); + MSWrite(current, high); + } +} + +_disused static void MSPushPointer(uint8_t *¤t, void *target) { + return MSPushPointer(current, reinterpret_cast(target)); +} + +_disused static void MSWriteCall(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xd0 | target & 0x07); +} + +_disused static void MSWriteCall(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) { + MSWrite(current, 0xe8); + MSWrite(current, target - (source + 5)); + } else { + MSPushPointer(current, target); + + MSWrite(current, 0x83); + MSWrite(current, 0xc4); + MSWrite(current, 0x08); + + MSWrite(current, 0x67); + MSWrite(current, 0xff); + MSWrite(current, 0x54); + MSWrite(current, 0x24); + MSWrite(current, 0xf8); + } +} + +template +_disused static void MSWriteCall(uint8_t *¤t, Type_ *target) { + return MSWriteCall(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) + MSWriteSkip(current, target - (source + 5)); + else { + MSPushPointer(current, target); + MSWrite(current, 0xc3); + } +} + +_disused static void MSWriteJump(uint8_t *¤t, void *target) { + return MSWriteJump(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xe0 | target & 0x07); +} + +_disused static void MSWritePop(uint8_t *¤t, uint8_t target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x58 | target & 0x07); +} + +_disused static size_t MSSizeOfPop(uint8_t target) { + return target >> 3 != 0 ? 2 : 1; +} + +_disused static void MSWritePush(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x50 | target & 0x07); +} + +_disused static void MSWriteAdd(uint8_t *¤t, I$r target, uint8_t source) { + MSWrite(current, 0x83); + MSWrite(current, 0xc4 | target & 0x07); + MSWrite(current, source); +} + +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, uintptr_t source) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2); + MSWrite(current, 0xb8 | target & 0x7); + MSWrite(current, source); +} + +template +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, Type_ *source) { + return MSWriteSet64(current, target, reinterpret_cast(source)); +} + +_disused static void MSWriteMove64(uint8_t *¤t, uint8_t source, uint8_t target) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3); + MSWrite(current, 0x8b); + MSWrite(current, (target & 0x07) << 3 | source & 0x07); +} + +_disused static size_t MSSizeOfMove64() { + return 3; +} + +#endif//SUBSTRATE_X86_HPP diff --git a/app/src/main/jni/src/Substrate/SymbolFinder.cpp b/app/src/main/jni/src/Substrate/SymbolFinder.cpp index e69de29..794a053 100644 --- a/app/src/main/jni/src/Substrate/SymbolFinder.cpp +++ b/app/src/main/jni/src/Substrate/SymbolFinder.cpp @@ -0,0 +1,430 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "SymbolFinder.h" + +#define TAG "MSHook" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) +/* memory map for libraries */ +#define MAX_NAME_LEN 256 +#define MEMORY_ONLY "[memory]" +struct mm { + char name[MAX_NAME_LEN]; + unsigned long start, end; +}; + +typedef struct symtab *symtab_t; +struct symlist { + Elf32_Sym *sym; /* symbols */ + char *str; /* symbol strings */ + unsigned num; /* number of symbols */ +}; +struct symtab { + struct symlist *st; /* "static" symbols */ + struct symlist *dyn; /* dynamic symbols */ +}; + +static void* xmalloc(size_t size) { + void *p; + p = malloc(size); + if (!p) { + printf("Out of memory\n"); + exit(1); + } + return p; +} + +static int my_pread(int fd, void *buf, size_t count, off_t offset) { + lseek(fd, offset, SEEK_SET); + return read(fd, buf, count); +} + +static struct symlist* get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) { + struct symlist *sl, *ret; + int rv; + + ret = NULL; + sl = (struct symlist *) xmalloc(sizeof(struct symlist)); + sl->str = NULL; + sl->sym = NULL; + + /* sanity */ + if (symh->sh_size % sizeof(Elf32_Sym)) { + //printf("elf_error\n"); + goto out; + } + + /* symbol table */ + sl->num = symh->sh_size / sizeof(Elf32_Sym); + sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size); + rv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset); + if (0 > rv) { + //perror("read"); + goto out; + } + if (rv != symh->sh_size) { + //printf("elf error\n"); + goto out; + } + + /* string table */ + sl->str = (char *) xmalloc(strh->sh_size); + rv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset); + if (0 > rv) { + //perror("read"); + goto out; + } + if (rv != strh->sh_size) { + //printf("elf error"); + goto out; + } + + ret = sl; + out: return ret; +} + +static int do_load(int fd, symtab_t symtab) { + int rv; + size_t size; + Elf32_Ehdr ehdr; + Elf32_Shdr *shdr = NULL, *p; + Elf32_Shdr *dynsymh, *dynstrh; + Elf32_Shdr *symh, *strh; + char *shstrtab = NULL; + int i; + int ret = -1; + + /* elf header */ + rv = read(fd, &ehdr, sizeof(ehdr)); + if (0 > rv) { + LOGD("read\n"); + goto out; + } + if (rv != sizeof(ehdr)) { + LOGD("elf error 1\n"); + goto out; + } + if (strncmp((const char *) ELFMAG, (const char *) ehdr.e_ident, SELFMAG)) { /* sanity */ + LOGD("not an elf\n"); + goto out; + } + if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */ + LOGD("elf error 2\n"); + goto out; + } + + /* section header table */ + size = ehdr.e_shentsize * ehdr.e_shnum; + shdr = (Elf32_Shdr *) xmalloc(size); + rv = my_pread(fd, shdr, size, ehdr.e_shoff); + if (0 > rv) { + LOGD("read\n"); + goto out; + } + if (rv != size) { + LOGD("elf error 3 %d %d\n", rv, size); + goto out; + } + + /* section header string table */ + size = shdr[ehdr.e_shstrndx].sh_size; + shstrtab = (char *) xmalloc(size); + rv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset); + if (0 > rv) { + LOGD("read\n"); + goto out; + } + if (rv != size) { + LOGD("elf error 4 %d %d\n", rv, size); + goto out; + } + + /* symbol table headers */ + symh = dynsymh = NULL; + strh = dynstrh = NULL; + for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++) + if (SHT_SYMTAB == p->sh_type) { + if (symh) { + LOGD("too many symbol tables\n"); + goto out; + } + symh = p; + } else if (SHT_DYNSYM == p->sh_type) { + if (dynsymh) { + LOGD("too many symbol tables\n"); + goto out; + } + dynsymh = p; + } else if (SHT_STRTAB == p->sh_type + && !strncmp(shstrtab + p->sh_name, ".strtab", 7)) { + if (strh) { + LOGD("too many string tables\n"); + goto out; + } + strh = p; + } else if (SHT_STRTAB == p->sh_type + && !strncmp(shstrtab + p->sh_name, ".dynstr", 7)) { + if (dynstrh) { + LOGD("too many string tables\n"); + goto out; + } + dynstrh = p; + } + /* sanity checks */ + if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) { + LOGD("bad dynamic symbol table\n"); + goto out; + } + if ((!symh && strh) || (symh && !strh)) { + LOGD("bad symbol table\n"); + goto out; + } + if (!dynsymh && !symh) { + LOGD("no symbol table\n"); + goto out; + } + + /* symbol tables */ + if (dynsymh) + symtab->dyn = get_syms(fd, dynsymh, dynstrh); + if (symh) + symtab->st = get_syms(fd, symh, strh); + ret = 0; + out: free(shstrtab); + free(shdr); + return ret; +} + +static symtab_t load_symtab(char *filename) { + int fd; + symtab_t symtab; + + symtab = (symtab_t) xmalloc(sizeof(*symtab)); + memset(symtab, 0, sizeof(*symtab)); + + fd = open(filename, O_RDONLY); + if (0 > fd) { + LOGE("%s open\n", __func__); + return NULL; + } + if (0 > do_load(fd, symtab)) { + LOGE("Error ELF parsing %s\n", filename); + free(symtab); + symtab = NULL; + } + close(fd); + return symtab; +} + +static int load_memmap(pid_t pid, struct mm *mm, int *nmmp) { + size_t buf_size = 0x40000; + char *p_buf = (char *) malloc(buf_size); // increase this if needed for larger "maps" + char name[MAX_NAME_LEN] = { 0 }; + char *p; + unsigned long start, end; + struct mm *m; + int nmm = 0; + int fd, rv; + int i; + + sprintf(p_buf, "/proc/%d/maps", pid); + fd = open(p_buf, O_RDONLY); + if (0 > fd) { + LOGE("Can't open %s for reading\n", p_buf); + free(p_buf); + return -1; + } + + /* Zero to ensure data is null terminated */ + memset(p_buf, 0, buf_size); + + p = p_buf; + while (1) { + rv = read(fd, p, buf_size - (p - p_buf)); + if (0 > rv) { + LOGE("%s read", __FUNCTION__); + free(p_buf); + return -1; + } + if (0 == rv) + break; + p += rv; + if (p - p_buf >= buf_size) { + LOGE("Too many memory mapping\n"); + free(p_buf); + return -1; + } + } + close(fd); + + p = strtok(p_buf, "\n"); + m = mm; + while (p) { + /* parse current map line */ + rv = sscanf(p, "%08lx-%08lx %*s %*s %*s %*s %s\n", &start, &end, name); + + p = strtok(NULL, "\n"); + + if (rv == 2) { + m = &mm[nmm++]; + m->start = start; + m->end = end; + memcpy(m->name, MEMORY_ONLY, sizeof(MEMORY_ONLY)); + continue; + } + + /* search backward for other mapping with same name */ + for (i = nmm - 1; i >= 0; i--) { + m = &mm[i]; + if (!strcmp(m->name, name)) + break; + } + + if (i >= 0) { + if (start < m->start) + m->start = start; + if (end > m->end) + m->end = end; + } else { + /* new entry */ + m = &mm[nmm++]; + m->start = start; + m->end = end; + memcpy(m->name, name, strlen(name)); + } + } + + *nmmp = nmm; + free(p_buf); + return 0; +} + +/* Find libc in MM, storing no more than LEN-1 chars of + its name in NAME and set START to its starting + address. If libc cannot be found return -1 and + leave NAME and START untouched. Otherwise return 0 + and null-terminated NAME. */ +static int find_libname(const char *libn, char *name, int len, unsigned long *start, + struct mm *mm, int nmm) { + int i; + struct mm *m; + char *p; + for (i = 0, m = mm; i < nmm; i++, m++) { + if (!strcmp(m->name, MEMORY_ONLY)) + continue; + p = strrchr(m->name, '/'); + if (!p) + continue; + p++; + if (strncmp(libn, p, strlen(libn))) + continue; + p += strlen(libn); + + /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */ + if (!strncmp("so", p, 2) || 1) // || (p[0] == '-' && isdigit(p[1]))) + break; + } + if (i >= nmm) + /* not found */ + return -1; + + *start = m->start; + strncpy(name, m->name, len); + if (strlen(m->name) >= len) + name[len - 1] = '\0'; + + mprotect((void*) m->start, m->end - m->start, + PROT_READ | PROT_WRITE | PROT_EXEC); + return 0; +} + +static int lookup2(struct symlist *sl, unsigned char type, char *name, + unsigned long *val) { + Elf32_Sym *p; + int len; + int i; + + len = strlen(name); + for (i = 0, p = sl->sym; i < sl->num; i++, p++) { + //LOGD("name: %s %x\n", sl->str+p->st_name, p->st_value) + if (!strncmp(sl->str + p->st_name, name, len) + && *(sl->str + p->st_name + len) == 0 + && ELF32_ST_TYPE(p->st_info) == type) { + //if (p->st_value != 0) { + *val = p->st_value; + return 0; + //} + } + } + return -1; +} + +static int lookup_sym(symtab_t s, unsigned char type, char *name, + unsigned long *val) { + if (s->dyn && !lookup2(s->dyn, type, name, val)) + return 0; + if (s->st && !lookup2(s->st, type, name, val)) + return 0; + return -1; +} + +static int lookup_func_sym(symtab_t s, char *name, unsigned long *val) { + return lookup_sym(s, STT_FUNC, name, val); +} + +int find_name(pid_t pid, const char *name, const char *libn, + unsigned long *addr) { + struct mm mm[1000] = { 0 }; + unsigned long libcaddr; + int nmm; + char libc[1024] = { 0 }; + symtab_t s; + + if (0 > load_memmap(pid, mm, &nmm)) { + LOGD("cannot read memory map\n"); + return -1; + } + if (0 + > find_libname((char *) libn, (char *) libc, sizeof(libc), + &libcaddr, mm, nmm)) { + LOGD("cannot find lib: %s\n", libn); + return -1; + } + //LOGD("lib: >%s<\n", libc) + s = load_symtab(libc); + if (!s) { + LOGD("cannot read symbol table\n"); + return -1; + } + if (0 > lookup_func_sym(s, (char *) name, addr)) { + LOGD("cannot find function: %s\n", name); + return -1; + } + *addr += libcaddr; + return 0; +} + +int find_libbase(pid_t pid, const char *libn, unsigned long *addr) { + struct mm mm[1000] = { 0 }; + unsigned long libcaddr; + int nmm; + char libc[1024] = { 0 }; + symtab_t s; + + if (0 > load_memmap(pid, mm, &nmm)) { + LOGD("cannot read memory map\n"); + return -1; + } + if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) { + LOGD("cannot find lib\n"); + return -1; + } + *addr = libcaddr; + return 0; +} diff --git a/app/src/main/jni/src/Substrate/SymbolFinder.h b/app/src/main/jni/src/Substrate/SymbolFinder.h index e69de29..7b99910 100644 --- a/app/src/main/jni/src/Substrate/SymbolFinder.h +++ b/app/src/main/jni/src/Substrate/SymbolFinder.h @@ -0,0 +1,8 @@ +#ifndef SYMBOL_FINDER +#define SYMBOL_FINDER + +#include + +extern int find_name(pid_t pid, const char *name,const char *libn, unsigned long *addr); +extern int find_libbase(pid_t pid, const char *libn, unsigned long *addr); +#endif \ No newline at end of file diff --git a/app/src/main/jni/src/Substrate/hde64.c b/app/src/main/jni/src/Substrate/hde64.c index e69de29..d69f0c6 100644 --- a/app/src/main/jni/src/Substrate/hde64.c +++ b/app/src/main/jni/src/Substrate/hde64.c @@ -0,0 +1,332 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include +#include + +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + memset(hs,0,sizeof(hde64s)); + char *tmp=(char*)hs; + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} diff --git a/app/src/main/jni/src/Substrate/hde64.h b/app/src/main/jni/src/Substrate/hde64.h index e69de29..2fcc4cb 100644 --- a/app/src/main/jni/src/Substrate/hde64.h +++ b/app/src/main/jni/src/Substrate/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/app/src/main/jni/src/Substrate/table64.h b/app/src/main/jni/src/Substrate/table64.h index e69de29..144f290 100644 --- a/app/src/main/jni/src/Substrate/table64.h +++ b/app/src/main/jni/src/Substrate/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/app/src/main/jni/src/Unity/Color.hpp b/app/src/main/jni/src/Unity/Color.hpp index e69de29..1ea0cb9 100644 --- a/app/src/main/jni/src/Unity/Color.hpp +++ b/app/src/main/jni/src/Unity/Color.hpp @@ -0,0 +1,50 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include + +struct Color +{ + union + { + struct + { + float R; + float G; + float B; + float A; + }; + float data[4]; + }; + + inline Color(); + inline Color(float r, float g, float b, float a); + + static inline Color red(); + static inline Color green(); + static inline Color blue(); + static inline Color white(); + static inline Color black(); + static inline Color purpleSense(); + static inline Color yellow(); + static inline Color magenta(); + static inline Color gray(); + static inline Color grey(); + static inline Color clear(); +}; + +Color::Color() : R(0), G(0), B(0), A(0) {} +Color::Color(float r, float g, float b, float a) : R(r), G(g), B(b), A(a) {} + +Color Color::red() { return Color(1,0,0,1); } +Color Color::green() { return Color(0,1,0,1); } +Color Color::blue() { return Color(0,0,1,1); } +Color Color::white() { return Color(1,1,1,1); } +Color Color::black() { return Color(0,0,0,1); } +Color Color::purpleSense() { return Color(0.6039215, 0.0705882, 0.7019607, 1); } +Color Color::yellow() { return Color(1,0.921569,0.0156863,1); } +Color Color::magenta() { return Color(1,0,1,1); } +Color Color::gray() { return Color(0.5,0.5,0.5,1); } +Color Color::grey() { return Color(0.5,0.5,0.5,1); } +Color Color::clear() { return Color(0,0,0,0.5); } \ No newline at end of file diff --git a/app/src/main/jni/src/Unity/Quaternion.hpp b/app/src/main/jni/src/Unity/Quaternion.hpp index e69de29..accdd22 100644 --- a/app/src/main/jni/src/Unity/Quaternion.hpp +++ b/app/src/main/jni/src/Unity/Quaternion.hpp @@ -0,0 +1,703 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include + +#define SMALL_float 0.0000000001 + + +/** + * Attempt to include a header file if the file exists. + * If the file does not exist, create a dummy data structure for that type. + * If it cannot be determined if it exists, just attempt to include it. + */ +#ifdef __has_include +# if __has_include("Vector3.hpp") +# include "Vector3.hpp" +# elif !defined(GMATH_VECTOR3) +#define GMATH_VECTOR3 + struct Vector3 + { + union + { + struct + { + float X; + float Y; + float Z; + }; + float data[3]; + }; + + inline Vector3() : X(0), Y(0), Z(0) {} + inline Vector3(float data[]) : X(data[0]), Y(data[1]), Z(data[2]) + {} + inline Vector3(float value) : X(value), Y(value), Z(value) {} + inline Vector3(float x, float y) : X(x), Y(y), Z(0) {} + inline Vector3(float x, float y, float z) : X(x), Y(y), Z(z) {} + + static inline Vector3 Cross(Vector3 lhs, Vector3 rhs) + { + float x = lhs.Y * rhs.Z - lhs.Z * rhs.Y; + float y = lhs.Z * rhs.X - lhs.X * rhs.Z; + float z = lhs.X * rhs.Y - lhs.Y * rhs.X; + return Vector3(x, y, z); + } + + static inline float Dot(Vector3 lhs, Vector3 rhs) + { + return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z; + } + + static inline Vector3 Normalized(Vector3 v) + { + float mag = sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z); + if (mag == 0) + return Vector3::Zero(); + return v / mag; + } + + static inline Vector3 Orthogonal(Vector3 v) + { + return v.Z < v.X ? + Vector3(v.Y, -v.X, 0) : Vector3(0, -v.Z, v.Y); + } + + static inline float SqrMagnitude(Vector3 v) + { + return v.X * v.X + v.Y * v.Y + v.Z * v.Z; + } + }; + + + inline Vector3 operator+(Vector3 lhs, const Vector3 rhs) + { + return Vector3(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z); + } + + inline Vector3 operator*(Vector3 lhs, const float rhs) + { + return Vector3(lhs.X * rhs, lhs.Y * rhs, lhs.Z * rhs); + } +# endif +#else +# include "Vector3.hpp" +#endif + + +struct Quaternion +{ + union + { + struct + { + float X; + float Y; + float Z; + float W; + }; + float data[4]; + }; + + + /** + * Constructors. + */ + inline Quaternion(); + inline Quaternion(float data[]); + inline Quaternion(Vector3 vector, float scalar); + inline Quaternion(float x, float y, float z, float w); + + + /** + * Constants for common quaternions. + */ + static inline Quaternion Identity(); + + + /** + * Returns the angle between two quaternions. + * The quaternions must be normalized. + * @param a: The first quaternion. + * @param b: The second quaternion. + * @return: A scalar value. + */ + static inline float Angle(Quaternion a, Quaternion b); + + /** + * Returns the conjugate of a quaternion. + * @param rotation: The quaternion in question. + * @return: A new quaternion. + */ + static inline Quaternion Conjugate(Quaternion rotation); + + /** + * Returns the dot product of two quaternions. + * @param lhs: The left side of the multiplication. + * @param rhs: The right side of the multiplication. + * @return: A scalar value. + */ + static inline float Dot(Quaternion lhs, Quaternion rhs); + + /** + * Creates a new quaternion from the angle-axis representation of + * a rotation. + * @param angle: The rotation angle in radians. + * @param axis: The vector about which the rotation occurs. + * @return: A new quaternion. + */ + static inline Quaternion FromAngleAxis(float angle, Vector3 axis); + + /** + * Create a new quaternion from the euler angle representation of + * a rotation. The z, x and y values represent rotations about those + * axis in that respective order. + * @param rotation: The x, y and z rotations. + * @return: A new quaternion. + */ + static inline Quaternion FromEuler(Vector3 rotation); + + /** + * Create a new quaternion from the euler angle representation of + * a rotation. The z, x and y values represent rotations about those + * axis in that respective order. + * @param x: The rotation about the x-axis in radians. + * @param y: The rotation about the y-axis in radians. + * @param z: The rotation about the z-axis in radians. + * @return: A new quaternion. + */ + static inline Quaternion FromEuler(float x, float y, float z); + + /** + * Create a quaternion rotation which rotates "fromVector" to "toVector". + * @param fromVector: The vector from which to start the rotation. + * @param toVector: The vector at which to end the rotation. + * @return: A new quaternion. + */ + static inline Quaternion FromToRotation(Vector3 fromVector, + Vector3 toVector); + + /** + * Returns the inverse of a rotation. + * @param rotation: The quaternion in question. + * @return: A new quaternion. + */ + static inline Quaternion Inverse(Quaternion rotation); + + /** + * Interpolates between a and b by t, which is clamped to the range [0-1]. + * The result is normalized before being returned. + * @param a: The starting rotation. + * @param b: The ending rotation. + * @return: A new quaternion. + */ + static inline Quaternion Lerp(Quaternion a, Quaternion b, float t); + + /** + * Interpolates between a and b by t. This normalizes the result when + * complete. + * @param a: The starting rotation. + * @param b: The ending rotation. + * @param t: The interpolation value. + * @return: A new quaternion. + */ + static inline Quaternion LerpUnclamped(Quaternion a, Quaternion b, + float t); + + /** + * Creates a rotation with the specified forward direction. This is the + * same as calling LookRotation with (0, 1, 0) as the upwards vector. + * The output is undefined for parallel vectors. + * @param forward: The forward direction to look toward. + * @return: A new quaternion. + */ + static inline Quaternion LookRotation(Vector3 forward); + + /** + * Creates a rotation with the specified forward and upwards directions. + * The output is undefined for parallel vectors. + * @param forward: The forward direction to look toward. + * @param upwards: The direction to treat as up. + * @return: A new quaternion. + */ + static inline Quaternion LookRotation(Vector3 forward, Vector3 upwards); + + /** + * Returns the norm of a quaternion. + * @param rotation: The quaternion in question. + * @return: A scalar value. + */ + static inline float Norm(Quaternion rotation); + + /** + * Returns a quaternion with identical rotation and a norm of one. + * @param rotation: The quaternion in question. + * @return: A new quaternion. + */ + static inline Quaternion Normalized(Quaternion rotation); + + /** + * Returns a new Quaternion created by rotating "from" towards "to" by + * "maxRadiansDelta". This will not overshoot, and if a negative delta is + * applied, it will rotate till completely opposite "to" and then stop. + * @param from: The rotation at which to start. + * @param to: The rotation at which to end. + # @param maxRadiansDelta: The maximum number of radians to rotate. + * @return: A new Quaternion. + */ + static inline Quaternion RotateTowards(Quaternion from, Quaternion to, + float maxRadiansDelta); + + /** + * Returns a new quaternion interpolated between a and b, using spherical + * linear interpolation. The variable t is clamped to the range [0-1]. The + * resulting quaternion will be normalized. + * @param a: The starting rotation. + * @param b: The ending rotation. + * @param t: The interpolation value. + * @return: A new quaternion. + */ + static inline Quaternion Slerp(Quaternion a, Quaternion b, float t); + + /** + * Returns a new quaternion interpolated between a and b, using spherical + * linear interpolation. The resulting quaternion will be normalized. + * @param a: The starting rotation. + * @param b: The ending rotation. + * @param t: The interpolation value. + * @return: A new quaternion. + */ + static inline Quaternion SlerpUnclamped(Quaternion a, Quaternion b, + float t); + + /** + * Outputs the angle axis representation of the provided quaternion. + * @param rotation: The input quaternion. + * @param angle: The output angle. + * @param axis: The output axis. + */ + static inline void ToAngleAxis(Quaternion rotation, float &angle, + Vector3 &axis); + + /** + * Returns the Euler angle representation of a rotation. The resulting + * vector contains the rotations about the z, x and y axis, in that order. + * @param rotation: The quaternion to convert. + * @return: A new vector. + */ + static inline Vector3 ToEuler(Quaternion rotation); + + /** + * Operator overloading. + */ + inline struct Quaternion& operator+=(const float rhs); + inline struct Quaternion& operator-=(const float rhs); + inline struct Quaternion& operator*=(const float rhs); + inline struct Quaternion& operator/=(const float rhs); + inline struct Quaternion& operator+=(const Quaternion rhs); + inline struct Quaternion& operator-=(const Quaternion rhs); + inline struct Quaternion& operator*=(const Quaternion rhs); +}; + +inline Quaternion operator-(Quaternion rhs); +inline Quaternion operator+(Quaternion lhs, const float rhs); +inline Quaternion operator-(Quaternion lhs, const float rhs); +inline Quaternion operator*(Quaternion lhs, const float rhs); +inline Quaternion operator/(Quaternion lhs, const float rhs); +inline Quaternion operator+(const float lhs, Quaternion rhs); +inline Quaternion operator-(const float lhs, Quaternion rhs); +inline Quaternion operator*(const float lhs, Quaternion rhs); +inline Quaternion operator/(const float lhs, Quaternion rhs); +inline Quaternion operator+(Quaternion lhs, const Quaternion rhs); +inline Quaternion operator-(Quaternion lhs, const Quaternion rhs); +inline Quaternion operator*(Quaternion lhs, const Quaternion rhs); +inline Vector3 operator*(Quaternion lhs, const Vector3 rhs); +inline bool operator==(const Quaternion lhs, const Quaternion rhs); +inline bool operator!=(const Quaternion lhs, const Quaternion rhs); + + + +/******************************************************************************* + * Implementation + */ + +Quaternion::Quaternion() : X(0), Y(0), Z(0), W(1) {} +Quaternion::Quaternion(float data[]) : X(data[0]), Y(data[1]), Z(data[2]), + W(data[3]) {} +Quaternion::Quaternion(Vector3 vector, float scalar) : X(vector.X), + Y(vector.Y), Z(vector.Z), W(scalar) {} +Quaternion::Quaternion(float x, float y, float z, float w) : X(x), Y(y), + Z(z), W(w) {} + + +Quaternion Quaternion::Identity() { return Quaternion(0, 0, 0, 1); } + + +float Quaternion::Angle(Quaternion a, Quaternion b) +{ + float dot = Dot(a, b); + return acos(fmin(fabs(dot), 1)) * 2; +} + +Quaternion Quaternion::Conjugate(Quaternion rotation) +{ + return Quaternion(-rotation.X, -rotation.Y, -rotation.Z, rotation.W); +} + +float Quaternion::Dot(Quaternion lhs, Quaternion rhs) +{ + return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z + lhs.W * rhs.W; +} + +Quaternion Quaternion::FromAngleAxis(float angle, Vector3 axis) +{ + Quaternion q; + float m = sqrt(axis.X * axis.X + axis.Y * axis.Y + axis.Z * axis.Z); + float s = sin(angle / 2) / m; + q.X = axis.X * s; + q.Y = axis.Y * s; + q.Z = axis.Z * s; + q.W = cos(angle / 2); + return q; +} + +Quaternion Quaternion::FromEuler(Vector3 rotation) +{ + return FromEuler(rotation.X, rotation.Y, rotation.Z); +} + +Quaternion Quaternion::FromEuler(float x, float y, float z) +{ + float cx = cos(x * 0.5); + float cy = cos(y * 0.5); + float cz = cos(z * 0.5); + float sx = sin(x * 0.5); + float sy = sin(y * 0.5); + float sz = sin(z * 0.5); + Quaternion q; + q.X = cx * sy * sz + cy * cz * sx; + q.Y = cx * cz * sy - cy * sx * sz; + q.Z = cx * cy * sz - cz * sx * sy; + q.W = sx * sy * sz + cx * cy * cz; + return q; +} + +Quaternion Quaternion::FromToRotation(Vector3 fromVector, Vector3 toVector) +{ + float dot = Vector3::Dot(fromVector, toVector); + float k = sqrt(Vector3::SqrMagnitude(fromVector) * + Vector3::SqrMagnitude(toVector)); + if (fabs(dot / k + 1) < 0.00001) + { + Vector3 ortho = Vector3::Orthogonal(fromVector); + return Quaternion(Vector3::Normalized(ortho), 0); + } + Vector3 cross = Vector3::Cross(fromVector, toVector); + return Normalized(Quaternion(cross, dot + k)); +} + +Quaternion Quaternion::Inverse(Quaternion rotation) +{ + float n = Norm(rotation); + return Conjugate(rotation) / (n * n); +} + +Quaternion Quaternion::Lerp(Quaternion a, Quaternion b, float t) +{ + if (t < 0) return Normalized(a); + else if (t > 1) return Normalized(b); + return LerpUnclamped(a, b, t); +} + +Quaternion Quaternion::LerpUnclamped(Quaternion a, Quaternion b, float t) +{ + Quaternion quaternion; + if (Dot(a, b) >= 0) + quaternion = a * (1 - t) + b * t; + else + quaternion = a * (1 - t) - b * t; + return Normalized(quaternion); +} + +Quaternion Quaternion::LookRotation(Vector3 forward) +{ + return LookRotation(forward, Vector3(0, 1, 0)); +} + +Quaternion Quaternion::LookRotation(Vector3 forward, Vector3 upwards) +{ + // Normalize inputs + forward = Vector3::Normalized(forward); + upwards = Vector3::Normalized(upwards); + // Don't allow zero vectors + if (Vector3::SqrMagnitude(forward) < SMALL_float || Vector3::SqrMagnitude(upwards) < SMALL_float) + return Quaternion::Identity(); + // Handle alignment with up direction + if (1 - fabs(Vector3::Dot(forward, upwards)) < SMALL_float) + return FromToRotation(Vector3::Forward(), forward); + // Get orthogonal vectors + Vector3 right = Vector3::Normalized(Vector3::Cross(upwards, forward)); + upwards = Vector3::Cross(forward, right); + // Calculate rotation + Quaternion quaternion; + float radicand = right.X + upwards.Y + forward.Z; + if (radicand > 0) + { + quaternion.W = sqrt(1.0 + radicand) * 0.5; + float recip = 1.0 / (4.0 * quaternion.W); + quaternion.X = (upwards.Z - forward.Y) * recip; + quaternion.Y = (forward.X - right.Z) * recip; + quaternion.Z = (right.Y - upwards.X) * recip; + } + else if (right.X >= upwards.Y && right.X >= forward.Z) + { + quaternion.X = sqrt(1.0 + right.X - upwards.Y - forward.Z) * 0.5; + float recip = 1.0 / (4.0 * quaternion.X); + quaternion.W = (upwards.Z - forward.Y) * recip; + quaternion.Z = (forward.X + right.Z) * recip; + quaternion.Y = (right.Y + upwards.X) * recip; + } + else if (upwards.Y > forward.Z) + { + quaternion.Y = sqrt(1.0 - right.X + upwards.Y - forward.Z) * 0.5; + float recip = 1.0 / (4.0 * quaternion.Y); + quaternion.Z = (upwards.Z + forward.Y) * recip; + quaternion.W = (forward.X - right.Z) * recip; + quaternion.X = (right.Y + upwards.X) * recip; + } + else + { + quaternion.Z = sqrt(1.0 - right.X - upwards.Y + forward.Z) * 0.5; + float recip = 1.0 / (4.0 * quaternion.Z); + quaternion.Y = (upwards.Z + forward.Y) * recip; + quaternion.X = (forward.X + right.Z) * recip; + quaternion.W = (right.Y - upwards.X) * recip; + } + return quaternion; +} + +float Quaternion::Norm(Quaternion rotation) +{ + return sqrt(rotation.X * rotation.X + + rotation.Y * rotation.Y + + rotation.Z * rotation.Z + + rotation.W * rotation.W); +} + +Quaternion Quaternion::Normalized(Quaternion rotation) +{ + return rotation / Norm(rotation); +} + +Quaternion Quaternion::RotateTowards(Quaternion from, Quaternion to, + float maxRadiansDelta) +{ + float angle = Quaternion::Angle(from, to); + if (angle == 0) + return to; + maxRadiansDelta = fmax(maxRadiansDelta, angle - M_PI); + float t = fmin(1, maxRadiansDelta / angle); + return Quaternion::SlerpUnclamped(from, to, t); +} + +Quaternion Quaternion::Slerp(Quaternion a, Quaternion b, float t) +{ + if (t < 0) return Normalized(a); + else if (t > 1) return Normalized(b); + return SlerpUnclamped(a, b, t); +} + +Quaternion Quaternion::SlerpUnclamped(Quaternion a, Quaternion b, float t) +{ + float n1; + float n2; + float n3 = Dot(a, b); + bool flag = false; + if (n3 < 0) + { + flag = true; + n3 = -n3; + } + if (n3 > 0.999999) + { + n2 = 1 - t; + n1 = flag ? -t : t; + } + else + { + float n4 = acos(n3); + float n5 = 1 / sin(n4); + n2 = sin((1 - t) * n4) * n5; + n1 = flag ? -sin(t * n4) * n5 : sin(t * n4) * n5; + } + Quaternion quaternion; + quaternion.X = (n2 * a.X) + (n1 * b.X); + quaternion.Y = (n2 * a.Y) + (n1 * b.Y); + quaternion.Z = (n2 * a.Z) + (n1 * b.Z); + quaternion.W = (n2 * a.W) + (n1 * b.W); + return Normalized(quaternion); +} + +void Quaternion::ToAngleAxis(Quaternion rotation, float &angle, Vector3 &axis) +{ + if (rotation.W > 1) + rotation = Normalized(rotation); + angle = 2 * acos(rotation.W); + float s = sqrt(1 - rotation.W * rotation.W); + if (s < 0.00001) { + axis.X = 1; + axis.Y = 0; + axis.Z = 0; + } else { + axis.X = rotation.X / s; + axis.Y = rotation.Y / s; + axis.Z = rotation.Z / s; + } +} + +Vector3 Quaternion::ToEuler(Quaternion rotation) +{ + float sqw = rotation.W * rotation.W; + float sqx = rotation.X * rotation.X; + float sqy = rotation.Y * rotation.Y; + float sqz = rotation.Z * rotation.Z; + // If normalized is one, otherwise is correction factor + float unit = sqx + sqy + sqz + sqw; + float test = rotation.X * rotation.W - rotation.Y * rotation.Z; + Vector3 v; + // Singularity at north pole + if (test > 0.4995f * unit) + { + v.Y = 2 * atan2(rotation.Y, rotation.X); + v.X = M_PI_2; + v.Z = 0; + return v; + } + // Singularity at south pole + if (test < -0.4995f * unit) + { + v.Y = -2 * atan2(rotation.Y, rotation.X); + v.X = -M_PI_2; + v.Z = 0; + return v; + } + // Yaw + v.Y = atan2(2 * rotation.W * rotation.Y + 2 * rotation.Z * rotation.X, + 1 - 2 * (rotation.X * rotation.X + rotation.Y * rotation.Y)); + // Pitch + v.X = asin(2 * (rotation.W * rotation.X - rotation.Y * rotation.Z)); + // Roll + v.Z = atan2(2 * rotation.W * rotation.Z + 2 * rotation.X * rotation.Y, + 1 - 2 * (rotation.Z * rotation.Z + rotation.X * rotation.X)); + return v; +} + +struct Quaternion& Quaternion::operator+=(const float rhs) +{ + X += rhs; + Y += rhs; + Z += rhs; + W += rhs; + return *this; +} + +struct Quaternion& Quaternion::operator-=(const float rhs) +{ + X -= rhs; + Y -= rhs; + Z -= rhs; + W -= rhs; + return *this; +} + +struct Quaternion& Quaternion::operator*=(const float rhs) +{ + X *= rhs; + Y *= rhs; + Z *= rhs; + W *= rhs; + return *this; +} + +struct Quaternion& Quaternion::operator/=(const float rhs) +{ + X /= rhs; + Y /= rhs; + Z /= rhs; + W /= rhs; + return *this; +} + +struct Quaternion& Quaternion::operator+=(const Quaternion rhs) +{ + X += rhs.X; + Y += rhs.Y; + Z += rhs.Z; + W += rhs.W; + return *this; +} + +struct Quaternion& Quaternion::operator-=(const Quaternion rhs) +{ + X -= rhs.X; + Y -= rhs.Y; + Z -= rhs.Z; + W -= rhs.W; + return *this; +} + +struct Quaternion& Quaternion::operator*=(const Quaternion rhs) +{ + Quaternion q; + q.W = W * rhs.W - X * rhs.X - Y * rhs.Y - Z * rhs.Z; + q.X = X * rhs.W + W * rhs.X + Y * rhs.Z - Z * rhs.Y; + q.Y = W * rhs.Y - X * rhs.Z + Y * rhs.W + Z * rhs.X; + q.Z = W * rhs.Z + X * rhs.Y - Y * rhs.X + Z * rhs.W; + *this = q; + return *this; +} + +Quaternion operator-(Quaternion rhs) { return rhs * -1; } +Quaternion operator+(Quaternion lhs, const float rhs) { return lhs += rhs; } +Quaternion operator-(Quaternion lhs, const float rhs) { return lhs -= rhs; } +Quaternion operator*(Quaternion lhs, const float rhs) { return lhs *= rhs; } +Quaternion operator/(Quaternion lhs, const float rhs) { return lhs /= rhs; } +Quaternion operator+(const float lhs, Quaternion rhs) { return rhs += lhs; } +Quaternion operator-(const float lhs, Quaternion rhs) { return rhs -= lhs; } +Quaternion operator*(const float lhs, Quaternion rhs) { return rhs *= lhs; } +Quaternion operator/(const float lhs, Quaternion rhs) { return rhs /= lhs; } +Quaternion operator+(Quaternion lhs, const Quaternion rhs) +{ + return lhs += rhs; +} +Quaternion operator-(Quaternion lhs, const Quaternion rhs) +{ + return lhs -= rhs; +} +Quaternion operator*(Quaternion lhs, const Quaternion rhs) +{ + return lhs *= rhs; +} + +Vector3 operator*(Quaternion lhs, const Vector3 rhs) +{ + Vector3 u = Vector3(lhs.X, lhs.Y, lhs.Z); + float s = lhs.W; + return u * (Vector3::Dot(u, rhs) * 2) + + rhs * (s * s - Vector3::Dot(u, u)) + + Vector3::Cross(u, rhs) * (2.0 * s); +} + +bool operator==(const Quaternion lhs, const Quaternion rhs) +{ + return lhs.X == rhs.X && + lhs.Y == rhs.Y && + lhs.Z == rhs.Z && + lhs.W == rhs.W; +} + +bool operator!=(const Quaternion lhs, const Quaternion rhs) +{ + return !(lhs == rhs); +} \ No newline at end of file diff --git a/app/src/main/jni/src/Unity/Rect.hpp b/app/src/main/jni/src/Unity/Rect.hpp index e69de29..04240eb 100644 --- a/app/src/main/jni/src/Unity/Rect.hpp +++ b/app/src/main/jni/src/Unity/Rect.hpp @@ -0,0 +1,27 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include + +struct Rect +{ + union + { + struct + { + float m_XMin; + float m_YMin; + float m_Width; + float m_Height; + }; + float data[4]; + }; + + inline Rect(); + inline Rect(float XMin, float YMin, float Width, float Height); +}; + +Rect::Rect() : m_XMin(0), m_YMin(0), m_Width(0), m_Height(0) {} +Rect::Rect(float XMin, float YMin, float Width, float Height) : m_XMin(XMin), m_YMin(YMin), m_Width(Width), m_Height(Height) {} + diff --git a/app/src/main/jni/src/Unity/Unity.h b/app/src/main/jni/src/Unity/Unity.h index e69de29..f2ac2fc 100644 --- a/app/src/main/jni/src/Unity/Unity.h +++ b/app/src/main/jni/src/Unity/Unity.h @@ -0,0 +1,218 @@ +#include +#include "Vector3.hpp" +#include "Vector2.hpp" +#include "Quaternion.hpp" +#include "Rect.hpp" +#include "Color.hpp" + +#define LOG_TAG "PusexPrivate.Xyz" + + +float NormalizeAngle (float angle){ + while (angle>360) + angle -= 360; + while (angle<0) + angle += 360; + return angle; +} + +Vector3 NormalizeAngles (Vector3 angles){ + angles.X = NormalizeAngle (angles.X); + angles.Y = NormalizeAngle (angles.Y); + angles.Z = NormalizeAngle (angles.Z); + return angles; +} + +Vector3 ToEulerRad(Quaternion q1){ + float Rad2Deg = 360.0 / (M_PI * 2.0); + + float sqw = q1.W * q1.W; + float sqx = q1.X * q1.X; + float sqy = q1.Y * q1.Y; + float sqz = q1.Z * q1.Z; + float unit = sqx + sqy + sqz + sqw; + float test = q1.X * q1.W - q1.Y * q1.Z; + Vector3 v; + + if (test>0.4995*unit) { + v.Y = 2.0 * atan2f (q1.Y, q1.X); + v.X = M_PI / 2.0; + v.Z = 0; + return NormalizeAngles(v * Rad2Deg); + } + if (test<-0.4995*unit) { + v.Y = -2.0 * atan2f (q1.Y, q1.X); + v.X = -M_PI / 2.0; + v.Z = 0; + return NormalizeAngles (v * Rad2Deg); + } + Quaternion q(q1.W, q1.Z, q1.X, q1.Y); + v.Y = atan2f (2.0 * q.X * q.W + 2.0 * q.Y * q.Z, 1 - 2.0 * (q.Z * q.Z + q.W * q.W)); // yaw + v.X = asinf (2.0 * (q.X * q.Z - q.W * q.Y)); // pitch + v.Z = atan2f (2.0 * q.X * q.Y + 2.0 * q.Z * q.W, 1 - 2.0 * (q.Y * q.Y + q.Z * q.Z)); // roll + return NormalizeAngles (v * Rad2Deg); +} + +Quaternion GetRotationToLocation(Vector3 targetLocation, float y_bias, Vector3 myLoc){ + return Quaternion::LookRotation((targetLocation + Vector3(0, y_bias, 0)) - myLoc, Vector3(0, 1, 0)); +} + +template +struct monoArray +{ + void* klass; + void* monitor; + void* bounds; + int max_length; + void* vector [1]; + int getLength() + { + return max_length; + } + T getPointer() + { + return (T)vector; + } +}; + +template +struct monoList { + void *unk0; + void *unk1; + monoArray *items; + int size; + int version; + + T getItems(){ + return items->getPointer(); + } + + int getSize(){ + return size; + } + + int getVersion(){ + return version; + } +}; + +template +struct monoDictionary { + void *unk0; + void *unk1; + monoArray *table; + monoArray *linkSlots; + monoArray *keys; + monoArray *values; + int touchedSlots; + int emptySlot; + int size; + + K getKeys(){ + return keys->getPointer(); + } + + V getValues(){ + return values->getPointer(); + } + + int getNumKeys(){ + return keys->getLength(); + } + + int getNumValues(){ + return values->getLength(); + } + + int getSize(){ + return size; + } +}; +union intfloat { + int i; + float f; +}; + +typedef struct _monoString +{ + void* klass; + void* monitor; + int length; + char chars[1]; + int getLength() + { + return length; + } + char* getChars() + { + return chars; + } +}monoString; + + +/* +Get the real value of an ObscuredInt. +Parameters: + - location: the location of the ObscuredInt +*/ +int GetObscuredIntValue(uint64_t location){ + int cryptoKey = *(int *)location; + int obfuscatedValue = *(int *)(location + 0x4); + + return obfuscatedValue ^ cryptoKey; +} + +int GetObscuredBoolValue(uint64_t location){ + int cryptoKey = *(int *)(location + 0x8); + int obfuscatedValue = *(int *)(location + 0xC); + obfuscatedValue ^= cryptoKey; + return obfuscatedValue; +} + +/* +Set the real value of an ObscuredInt. +Parameters: + - location: the location of the ObscuredInt + - value: the value we're setting the ObscuredInt to +*/ +void SetObscuredIntValue(uint64_t location, int value){ + int cryptoKey = *(int *)location; + + *(int *)(location + 0x4) = value ^ cryptoKey; +} + +/* +Get the real value of an ObscuredFloat. +Parameters: + - location: the location of the ObscuredFloat +*/ +float GetObscuredFloatValue(uint64_t location){ + int cryptoKey = *(int *)location; + int obfuscatedValue = *(int *)(location + 0x4); + + /* use this intfloat to set the integer representation of our parameter value, which will also set the float value */ + intfloat IF; + IF.i = obfuscatedValue ^ cryptoKey; + + return IF.f; +} + +/* +Set the real value of an ObscuredFloat. +Parameters: + - location: the location of the ObscuredFloat + - value: the value we're setting the ObscuredFloat to +*/ +void SetObscuredFloatValue(uint64_t location, float value){ + int cryptoKey = *(int *)location; + + /* use this intfloat to get the integer representation of our parameter value */ + intfloat IF; + IF.f = value; + + /* use this intfloat to generate our hacked ObscuredFloat */ + intfloat IF2; + IF2.i = IF.i ^ cryptoKey; + + *(float *)(location + 0x4) = IF2.f; +} \ No newline at end of file diff --git a/app/src/main/jni/src/Unity/Vector2.hpp b/app/src/main/jni/src/Unity/Vector2.hpp index e69de29..212580b 100644 --- a/app/src/main/jni/src/Unity/Vector2.hpp +++ b/app/src/main/jni/src/Unity/Vector2.hpp @@ -0,0 +1,533 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include + + +struct Vector2 +{ + union + { + struct + { + float X; + float Y; + }; + float data[2]; + }; + + + /** + * Constructors. + */ + inline Vector2(); + inline Vector2(float data[]); + inline Vector2(float value); + inline Vector2(float x, float y); + + + /** + * Constants for common vectors. + */ + static inline Vector2 Zero(); + static inline Vector2 One(); + static inline Vector2 Right(); + static inline Vector2 Left(); + static inline Vector2 Up(); + static inline Vector2 Down(); + + + /** + * Returns the angle between two vectors in radians. + * @param a: The first vector. + * @param b: The second vector. + * @return: A scalar value. + */ + static inline float Angle(Vector2 a, Vector2 b); + + /** + * Returns a vector with its magnitude clamped to maxLength. + * @param vector: The target vector. + * @param maxLength: The maximum length of the return vector. + * @return: A new vector. + */ + static inline Vector2 ClampMagnitude(Vector2 vector, float maxLength); + + /** + * Returns the component of a in the direction of b (scalar projection). + * @param a: The target vector. + * @param b: The vector being compared against. + * @return: A scalar value. + */ + static inline float Component(Vector2 a, Vector2 b); + + /** + * Returns the distance between a and b. + * @param a: The first point. + * @param b: The second point. + * @return: A scalar value. + */ + static inline float Distance(Vector2 a, Vector2 b); + + /** + * Returns the dot product of two vectors. + * @param lhs: The left side of the multiplication. + * @param rhs: The right side of the multiplication. + * @return: A scalar value. + */ + static inline float Dot(Vector2 lhs, Vector2 rhs); + + /** + * Converts a polar representation of a vector into cartesian + * coordinates. + * @param rad: The magnitude of the vector. + * @param theta: The angle from the X axis. + * @return: A new vector. + */ + static inline Vector2 FromPolar(float rad, float theta); + + /** + * Returns a vector linearly interpolated between a and b, moving along + * a straight line. The vector is clamped to never go beyond the end points. + * @param a: The starting point. + * @param b: The ending point. + * @param t: The interpolation value [0-1]. + * @return: A new vector. + */ + static inline Vector2 Lerp(Vector2 a, Vector2 b, float t); + + /** + * Returns a vector linearly interpolated between a and b, moving along + * a straight line. + * @param a: The starting point. + * @param b: The ending point. + * @param t: The interpolation value [0-1] (no actual bounds). + * @return: A new vector. + */ + static inline Vector2 LerpUnclamped(Vector2 a, Vector2 b, float t); + + /** + * Returns the magnitude of a vector. + * @param v: The vector in question. + * @return: A scalar value. + */ + static inline float Magnitude(Vector2 v); + + /** + * Returns a vector made from the largest components of two other vectors. + * @param a: The first vector. + * @param b: The second vector. + * @return: A new vector. + */ + static inline Vector2 Max(Vector2 a, Vector2 b); + + /** + * Returns a vector made from the smallest components of two other vectors. + * @param a: The first vector. + * @param b: The second vector. + * @return: A new vector. + */ + static inline Vector2 Min(Vector2 a, Vector2 b); + + /** + * Returns a vector "maxDistanceDelta" units closer to the target. This + * interpolation is in a straight line, and will not overshoot. + * @param current: The current position. + * @param target: The destination position. + * @param maxDistanceDelta: The maximum distance to move. + * @return: A new vector. + */ + static inline Vector2 MoveTowards(Vector2 current, Vector2 target, + float maxDistanceDelta); + + /** + * Returns a new vector with magnitude of one. + * @param v: The vector in question. + * @return: A new vector. + */ + static inline Vector2 Normalized(Vector2 v); + + /** + * Creates a new coordinate system out of the two vectors. + * Normalizes "normal" and normalizes "tangent" and makes it orthogonal to + * "normal".. + * @param normal: A reference to the first axis vector. + * @param tangent: A reference to the second axis vector. + */ + static inline void OrthoNormalize(Vector2 &normal, Vector2 &tangent); + + /** + * Returns the vector projection of a onto b. + * @param a: The target vector. + * @param b: The vector being projected onto. + * @return: A new vector. + */ + static inline Vector2 Project(Vector2 a, Vector2 b); + + /** + * Returns a vector reflected about the provided line. + * This behaves as if there is a plane with the line as its normal, and the + * vector comes in and bounces off this plane. + * @param vector: The vector traveling inward at the imaginary plane. + * @param line: The line about which to reflect. + * @return: A new vector pointing outward from the imaginary plane. + */ + static inline Vector2 Reflect(Vector2 vector, Vector2 line); + + /** + * Returns the vector rejection of a on b. + * @param a: The target vector. + * @param b: The vector being projected onto. + * @return: A new vector. + */ + static inline Vector2 Reject(Vector2 a, Vector2 b); + + /** + * Rotates vector "current" towards vector "target" by "maxRadiansDelta". + * This treats the vectors as directions and will linearly interpolate + * between their magnitudes by "maxMagnitudeDelta". This function does not + * overshoot. If a negative delta is supplied, it will rotate away from + * "target" until it is pointing the opposite direction, but will not + * overshoot that either. + * @param current: The starting direction. + * @param target: The destination direction. + * @param maxRadiansDelta: The maximum number of radians to rotate. + * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation. + * @return: A new vector. + */ + static inline Vector2 RotateTowards(Vector2 current, Vector2 target, + float maxRadiansDelta, + float maxMagnitudeDelta); + + /** + * Multiplies two vectors component-wise. + * @param a: The lhs of the multiplication. + * @param b: The rhs of the multiplication. + * @return: A new vector. + */ + static inline Vector2 Scale(Vector2 a, Vector2 b); + + /** + * Returns a vector rotated towards b from a by the percent t. + * Since interpolation is done spherically, the vector moves at a constant + * angular velocity. This rotation is clamped to 0 <= t <= 1. + * @param a: The starting direction. + * @param b: The ending direction. + * @param t: The interpolation value [0-1]. + */ + static inline Vector2 Slerp(Vector2 a, Vector2 b, float t); + + /** + * Returns a vector rotated towards b from a by the percent t. + * Since interpolation is done spherically, the vector moves at a constant + * angular velocity. This rotation is unclamped. + * @param a: The starting direction. + * @param b: The ending direction. + * @param t: The interpolation value [0-1]. + */ + static inline Vector2 SlerpUnclamped(Vector2 a, Vector2 b, float t); + + /** + * Returns the squared magnitude of a vector. + * This is useful when comparing relative lengths, where the exact length + * is not important, and much time can be saved by not calculating the + * square root. + * @param v: The vector in question. + * @return: A scalar value. + */ + static inline float SqrMagnitude(Vector2 v); + + /** + * Calculates the polar coordinate space representation of a vector. + * @param vector: The vector to convert. + * @param rad: The magnitude of the vector. + * @param theta: The angle from the X axis. + */ + static inline void ToPolar(Vector2 vector, float &rad, float &theta); + + + /** + * Operator overloading. + */ + inline struct Vector2& operator+=(const float rhs); + inline struct Vector2& operator-=(const float rhs); + inline struct Vector2& operator*=(const float rhs); + inline struct Vector2& operator/=(const float rhs); + inline struct Vector2& operator+=(const Vector2 rhs); + inline struct Vector2& operator-=(const Vector2 rhs); +}; + +inline Vector2 operator-(Vector2 rhs); +inline Vector2 operator+(Vector2 lhs, const float rhs); +inline Vector2 operator-(Vector2 lhs, const float rhs); +inline Vector2 operator*(Vector2 lhs, const float rhs); +inline Vector2 operator/(Vector2 lhs, const float rhs); +inline Vector2 operator+(const float lhs, Vector2 rhs); +inline Vector2 operator-(const float lhs, Vector2 rhs); +inline Vector2 operator*(const float lhs, Vector2 rhs); +inline Vector2 operator/(const float lhs, Vector2 rhs); +inline Vector2 operator+(Vector2 lhs, const Vector2 rhs); +inline Vector2 operator-(Vector2 lhs, const Vector2 rhs); +inline bool operator==(const Vector2 lhs, const Vector2 rhs); +inline bool operator!=(const Vector2 lhs, const Vector2 rhs); + + + +/******************************************************************************* + * Implementation + */ + +Vector2::Vector2() : X(0), Y(0) {} +Vector2::Vector2(float data[]) : X(data[0]), Y(data[1]) {} +Vector2::Vector2(float value) : X(value), Y(value) {} +Vector2::Vector2(float x, float y) : X(x), Y(y) {} + + +Vector2 Vector2::Zero() { return Vector2(0, 0); } +Vector2 Vector2::One() { return Vector2(1, 1); } +Vector2 Vector2::Right() { return Vector2(1, 0); } +Vector2 Vector2::Left() { return Vector2(-1, 0); } +Vector2 Vector2::Up() { return Vector2(0, 1); } +Vector2 Vector2::Down() { return Vector2(0, -1); } + + +float Vector2::Angle(Vector2 a, Vector2 b) +{ + float v = Dot(a, b) / (Magnitude(a) * Magnitude(b)); + v = fmax(v, -1.0); + v = fmin(v, 1.0); + return acos(v); +} + +Vector2 Vector2::ClampMagnitude(Vector2 vector, float maxLength) +{ + float length = Magnitude(vector); + if (length > maxLength) + vector *= maxLength / length; + return vector; +} + +float Vector2::Component(Vector2 a, Vector2 b) +{ + return Dot(a, b) / Magnitude(b); +} + +float Vector2::Distance(Vector2 a, Vector2 b) +{ + return Vector2::Magnitude(a - b); +} + +float Vector2::Dot(Vector2 lhs, Vector2 rhs) +{ + return lhs.X * rhs.X + lhs.Y * rhs.Y; +} + +Vector2 Vector2::FromPolar(float rad, float theta) +{ + Vector2 v; + v.X = rad * cos(theta); + v.Y = rad * sin(theta); + return v; +} + +Vector2 Vector2::Lerp(Vector2 a, Vector2 b, float t) +{ + if (t < 0) return a; + else if (t > 1) return b; + return LerpUnclamped(a, b, t); +} + +Vector2 Vector2::LerpUnclamped(Vector2 a, Vector2 b, float t) +{ + return (b - a) * t + a; +} + +float Vector2::Magnitude(Vector2 v) +{ + return sqrt(SqrMagnitude(v)); +} + +Vector2 Vector2::Max(Vector2 a, Vector2 b) +{ + float x = a.X > b.X ? a.X : b.X; + float y = a.Y > b.Y ? a.Y : b.Y; + return Vector2(x, y); +} + +Vector2 Vector2::Min(Vector2 a, Vector2 b) +{ + float x = a.X > b.X ? b.X : a.X; + float y = a.Y > b.Y ? b.Y : a.Y; + return Vector2(x, y); +} + +Vector2 Vector2::MoveTowards(Vector2 current, Vector2 target, + float maxDistanceDelta) +{ + Vector2 d = target - current; + float m = Magnitude(d); + if (m < maxDistanceDelta || m == 0) + return target; + return current + (d * maxDistanceDelta / m); +} + +Vector2 Vector2::Normalized(Vector2 v) +{ + float mag = Magnitude(v); + if (mag == 0) + return Vector2::Zero(); + return v / mag; +} + +void Vector2::OrthoNormalize(Vector2 &normal, Vector2 &tangent) +{ + normal = Normalized(normal); + tangent = Reject(tangent, normal); + tangent = Normalized(tangent); +} + +Vector2 Vector2::Project(Vector2 a, Vector2 b) +{ + float m = Magnitude(b); + return Dot(a, b) / (m * m) * b; +} + +Vector2 Vector2::Reflect(Vector2 vector, Vector2 planeNormal) +{ + return vector - 2 * Project(vector, planeNormal); +} + +Vector2 Vector2::Reject(Vector2 a, Vector2 b) +{ + return a - Project(a, b); +} + +Vector2 Vector2::RotateTowards(Vector2 current, Vector2 target, + float maxRadiansDelta, + float maxMagnitudeDelta) +{ + float magCur = Magnitude(current); + float magTar = Magnitude(target); + float newMag = magCur + maxMagnitudeDelta * + ((magTar > magCur) - (magCur > magTar)); + newMag = fmin(newMag, fmax(magCur, magTar)); + newMag = fmax(newMag, fmin(magCur, magTar)); + + float totalAngle = Angle(current, target) - maxRadiansDelta; + if (totalAngle <= 0) + return Normalized(target) * newMag; + else if (totalAngle >= M_PI) + return Normalized(-target) * newMag; + + float axis = current.X * target.Y - current.Y * target.X; + axis = axis / fabs(axis); + if (!(1 - fabs(axis) < 0.00001)) + axis = 1; + current = Normalized(current); + Vector2 newVector = current * cos(maxRadiansDelta) + + Vector2(-current.Y, current.X) * sin(maxRadiansDelta) * axis; + return newVector * newMag; +} + +Vector2 Vector2::Scale(Vector2 a, Vector2 b) +{ + return Vector2(a.X * b.X, a.Y * b.Y); +} + +Vector2 Vector2::Slerp(Vector2 a, Vector2 b, float t) +{ + if (t < 0) return a; + else if (t > 1) return b; + return SlerpUnclamped(a, b, t); +} + +Vector2 Vector2::SlerpUnclamped(Vector2 a, Vector2 b, float t) +{ + float magA = Magnitude(a); + float magB = Magnitude(b); + a /= magA; + b /= magB; + float dot = Dot(a, b); + dot = fmax(dot, -1.0); + dot = fmin(dot, 1.0); + float theta = acos(dot) * t; + Vector2 relativeVec = Normalized(b - a * dot); + Vector2 newVec = a * cos(theta) + relativeVec * sin(theta); + return newVec * (magA + (magB - magA) * t); +} + +float Vector2::SqrMagnitude(Vector2 v) +{ + return v.X * v.X + v.Y * v.Y; +} + +void Vector2::ToPolar(Vector2 vector, float &rad, float &theta) +{ + rad = Magnitude(vector); + theta = atan2(vector.Y, vector.X); +} + + +struct Vector2& Vector2::operator+=(const float rhs) +{ + X += rhs; + Y += rhs; + return *this; +} + +struct Vector2& Vector2::operator-=(const float rhs) +{ + X -= rhs; + Y -= rhs; + return *this; +} + +struct Vector2& Vector2::operator*=(const float rhs) +{ + X *= rhs; + Y *= rhs; + return *this; +} + +struct Vector2& Vector2::operator/=(const float rhs) +{ + X /= rhs; + Y /= rhs; + return *this; +} + +struct Vector2& Vector2::operator+=(const Vector2 rhs) +{ + X += rhs.X; + Y += rhs.Y; + return *this; +} + +struct Vector2& Vector2::operator-=(const Vector2 rhs) +{ + X -= rhs.X; + Y -= rhs.Y; + return *this; +} + +Vector2 operator-(Vector2 rhs) { return rhs * -1; } +Vector2 operator+(Vector2 lhs, const float rhs) { return lhs += rhs; } +Vector2 operator-(Vector2 lhs, const float rhs) { return lhs -= rhs; } +Vector2 operator*(Vector2 lhs, const float rhs) { return lhs *= rhs; } +Vector2 operator/(Vector2 lhs, const float rhs) { return lhs /= rhs; } +Vector2 operator+(const float lhs, Vector2 rhs) { return rhs += lhs; } +Vector2 operator-(const float lhs, Vector2 rhs) { return rhs -= lhs; } +Vector2 operator*(const float lhs, Vector2 rhs) { return rhs *= lhs; } +Vector2 operator/(const float lhs, Vector2 rhs) { return rhs /= lhs; } +Vector2 operator+(Vector2 lhs, const Vector2 rhs) { return lhs += rhs; } +Vector2 operator-(Vector2 lhs, const Vector2 rhs) { return lhs -= rhs; } + +bool operator==(const Vector2 lhs, const Vector2 rhs) +{ + return lhs.X == rhs.X && lhs.Y == rhs.Y; +} + +bool operator!=(const Vector2 lhs, const Vector2 rhs) +{ + return !(lhs == rhs); +} \ No newline at end of file diff --git a/app/src/main/jni/src/Unity/Vector3.hpp b/app/src/main/jni/src/Unity/Vector3.hpp index e69de29..61e0dcf 100644 --- a/app/src/main/jni/src/Unity/Vector3.hpp +++ b/app/src/main/jni/src/Unity/Vector3.hpp @@ -0,0 +1,630 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include + + +struct Vector3 +{ + union + { + struct + { + float X; + float Y; + float Z; + }; + float data[3]; + }; + + + /** + * Constructors. + */ + inline Vector3(); + inline Vector3(float data[]); + inline Vector3(float value); + inline Vector3(float x, float y); + inline Vector3(float x, float y, float z); + + + /** + * Constants for common vectors. + */ + static inline Vector3 Zero(); + static inline Vector3 One(); + static inline Vector3 Right(); + static inline Vector3 Left(); + static inline Vector3 Up(); + static inline Vector3 Down(); + static inline Vector3 Forward(); + static inline Vector3 Backward(); + + + /** + * Returns the angle between two vectors in radians. + * @param a: The first vector. + * @param b: The second vector. + * @return: A scalar value. + */ + static inline float Angle(Vector3 a, Vector3 b); + + /** + * Returns a vector with its magnitude clamped to maxLength. + * @param vector: The target vector. + * @param maxLength: The maximum length of the return vector. + * @return: A new vector. + */ + static inline Vector3 ClampMagnitude(Vector3 vector, float maxLength); + + /** + * Returns the component of a in the direction of b (scalar projection). + * @param a: The target vector. + * @param b: The vector being compared against. + * @return: A scalar value. + */ + static inline float Component(Vector3 a, Vector3 b); + + /** + * Returns the cross product of two vectors. + * @param lhs: The left side of the multiplication. + * @param rhs: The right side of the multiplication. + * @return: A new vector. + */ + static inline Vector3 Cross(Vector3 lhs, Vector3 rhs); + + /** + * Returns the distance between a and b. + * @param a: The first point. + * @param b: The second point. + * @return: A scalar value. + */ + static inline float Distance(Vector3 a, Vector3 b); + + static inline char ToChar(Vector3 a); + + /** + * Returns the dot product of two vectors. + * @param lhs: The left side of the multiplication. + * @param rhs: The right side of the multiplication. + * @return: A scalar value. + */ + static inline float Dot(Vector3 lhs, Vector3 rhs); + + /** + * Converts a spherical representation of a vector into cartesian + * coordinates. + * This uses the ISO convention (radius r, inclination theta, azimuth phi). + * @param rad: The magnitude of the vector. + * @param theta: The angle in the XY plane from the X axis. + * @param phi: The angle from the positive Z axis to the vector. + * @return: A new vector. + */ + static inline Vector3 FromSpherical(float rad, float theta, float phi); + + /** + * Returns a vector linearly interpolated between a and b, moving along + * a straight line. The vector is clamped to never go beyond the end points. + * @param a: The starting point. + * @param b: The ending point. + * @param t: The interpolation value [0-1]. + * @return: A new vector. + */ + static inline Vector3 Lerp(Vector3 a, Vector3 b, float t); + + /** + * Returns a vector linearly interpolated between a and b, moving along + * a straight line. + * @param a: The starting point. + * @param b: The ending point. + * @param t: The interpolation value [0-1] (no actual bounds). + * @return: A new vector. + */ + static inline Vector3 LerpUnclamped(Vector3 a, Vector3 b, float t); + + /** + * Returns the magnitude of a vector. + * @param v: The vector in question. + * @return: A scalar value. + */ + static inline float Magnitude(Vector3 v); + + /** + * Returns a vector made from the largest components of two other vectors. + * @param a: The first vector. + * @param b: The second vector. + * @return: A new vector. + */ + static inline Vector3 Max(Vector3 a, Vector3 b); + + /** + * Returns a vector made from the smallest components of two other vectors. + * @param a: The first vector. + * @param b: The second vector. + * @return: A new vector. + */ + static inline Vector3 Min(Vector3 a, Vector3 b); + + /** + * Returns a vector "maxDistanceDelta" units closer to the target. This + * interpolation is in a straight line, and will not overshoot. + * @param current: The current position. + * @param target: The destination position. + * @param maxDistanceDelta: The maximum distance to move. + * @return: A new vector. + */ + static inline Vector3 MoveTowards(Vector3 current, Vector3 target, + float maxDistanceDelta); + + /** + * Returns a new vector with magnitude of one. + * @param v: The vector in question. + * @return: A new vector. + */ + static inline Vector3 Normalized(Vector3 v); + + /** + * Returns an arbitrary vector orthogonal to the input. + * This vector is not normalized. + * @param v: The input vector. + * @return: A new vector. + */ + static inline Vector3 Orthogonal(Vector3 v); + + /** + * Creates a new coordinate system out of the three vectors. + * Normalizes "normal", normalizes "tangent" and makes it orthogonal to + * "normal" and normalizes "binormal" and makes it orthogonal to both + * "normal" and "tangent". + * @param normal: A reference to the first axis vector. + * @param tangent: A reference to the second axis vector. + * @param binormal: A reference to the third axis vector. + */ + static inline void OrthoNormalize(Vector3 &normal, Vector3 &tangent, + Vector3 &binormal); + + /** + * Returns the vector projection of a onto b. + * @param a: The target vector. + * @param b: The vector being projected onto. + * @return: A new vector. + */ + static inline Vector3 Project(Vector3 a, Vector3 b); + + /** + * Returns a vector projected onto a plane orthogonal to "planeNormal". + * This can be visualized as the shadow of the vector onto the plane, if + * the light source were in the direction of the plane normal. + * @param vector: The vector to project. + * @param planeNormal: The normal of the plane onto which to project. + * @param: A new vector. + */ + static inline Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal); + + /** + * Returns a vector reflected off the plane orthogonal to the normal. + * The input vector is pointed inward, at the plane, and the return vector + * is pointed outward from the plane, like a beam of light hitting and then + * reflecting off a mirror. + * @param vector: The vector traveling inward at the plane. + * @param planeNormal: The normal of the plane off of which to reflect. + * @return: A new vector pointing outward from the plane. + */ + static inline Vector3 Reflect(Vector3 vector, Vector3 planeNormal); + + /** + * Returns the vector rejection of a on b. + * @param a: The target vector. + * @param b: The vector being projected onto. + * @return: A new vector. + */ + static inline Vector3 Reject(Vector3 a, Vector3 b); + + /** + * Rotates vector "current" towards vector "target" by "maxRadiansDelta". + * This treats the vectors as directions and will linearly interpolate + * between their magnitudes by "maxMagnitudeDelta". This function does not + * overshoot. If a negative delta is supplied, it will rotate away from + * "target" until it is pointing the opposite direction, but will not + * overshoot that either. + * @param current: The starting direction. + * @param target: The destination direction. + * @param maxRadiansDelta: The maximum number of radians to rotate. + * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation. + * @return: A new vector. + */ + static inline Vector3 RotateTowards(Vector3 current, Vector3 target, + float maxRadiansDelta, + float maxMagnitudeDelta); + + /** + * Multiplies two vectors element-wise. + * @param a: The lhs of the multiplication. + * @param b: The rhs of the multiplication. + * @return: A new vector. + */ + static inline Vector3 Scale(Vector3 a, Vector3 b); + + /** + * Returns a vector rotated towards b from a by the percent t. + * Since interpolation is done spherically, the vector moves at a constant + * angular velocity. This rotation is clamped to 0 <= t <= 1. + * @param a: The starting direction. + * @param b: The ending direction. + * @param t: The interpolation value [0-1]. + */ + static inline Vector3 Slerp(Vector3 a, Vector3 b, float t); + + /** + * Returns a vector rotated towards b from a by the percent t. + * Since interpolation is done spherically, the vector moves at a constant + * angular velocity. This rotation is unclamped. + * @param a: The starting direction. + * @param b: The ending direction. + * @param t: The interpolation value [0-1]. + */ + static inline Vector3 SlerpUnclamped(Vector3 a, Vector3 b, float t); + + /** + * Returns the squared magnitude of a vector. + * This is useful when comparing relative lengths, where the exact length + * is not important, and much time can be saved by not calculating the + * square root. + * @param v: The vector in question. + * @return: A scalar value. + */ + static inline float SqrMagnitude(Vector3 v); + + /** + * Calculates the spherical coordinate space representation of a vector. + * This uses the ISO convention (radius r, inclination theta, azimuth phi). + * @param vector: The vector to convert. + * @param rad: The magnitude of the vector. + * @param theta: The angle in the XY plane from the X axis. + * @param phi: The angle from the positive Z axis to the vector. + */ + static inline void ToSpherical(Vector3 vector, float &rad, float &theta, + float &phi); + + + /** + * Operator overloading. + */ + inline struct Vector3& operator+=(const float rhs); + inline struct Vector3& operator-=(const float rhs); + inline struct Vector3& operator*=(const float rhs); + inline struct Vector3& operator/=(const float rhs); + inline struct Vector3& operator+=(const Vector3 rhs); + inline struct Vector3& operator-=(const Vector3 rhs); +}; + +inline Vector3 operator-(Vector3 rhs); +inline Vector3 operator+(Vector3 lhs, const float rhs); +inline Vector3 operator-(Vector3 lhs, const float rhs); +inline Vector3 operator*(Vector3 lhs, const float rhs); +inline Vector3 operator/(Vector3 lhs, const float rhs); +inline Vector3 operator+(const float lhs, Vector3 rhs); +inline Vector3 operator-(const float lhs, Vector3 rhs); +inline Vector3 operator*(const float lhs, Vector3 rhs); +inline Vector3 operator/(const float lhs, Vector3 rhs); +inline Vector3 operator+(Vector3 lhs, const Vector3 rhs); +inline Vector3 operator-(Vector3 lhs, const Vector3 rhs); +inline bool operator==(const Vector3 lhs, const Vector3 rhs); +inline bool operator!=(const Vector3 lhs, const Vector3 rhs); + + + +/******************************************************************************* + * Implementation + */ + +Vector3::Vector3() : X(0), Y(0), Z(0) {} +Vector3::Vector3(float data[]) : X(data[0]), Y(data[1]), Z(data[2]) {} +Vector3::Vector3(float value) : X(value), Y(value), Z(value) {} +Vector3::Vector3(float x, float y) : X(x), Y(y), Z(0) {} +Vector3::Vector3(float x, float y, float z) : X(x), Y(y), Z(z) {} + + +Vector3 Vector3::Zero() { return Vector3(0, 0, 0); } +Vector3 Vector3::One() { return Vector3(1, 1, 1); } +Vector3 Vector3::Right() { return Vector3(1, 0, 0); } +Vector3 Vector3::Left() { return Vector3(-1, 0, 0); } +Vector3 Vector3::Up() { return Vector3(0, 1, 0); } +Vector3 Vector3::Down() { return Vector3(0, -1, 0); } +Vector3 Vector3::Forward() { return Vector3(0, 0, 1); } +Vector3 Vector3::Backward() { return Vector3(0, 0, -1); } + + +float Vector3::Angle(Vector3 a, Vector3 b) +{ + float v = Dot(a, b) / (Magnitude(a) * Magnitude(b)); + v = fmax(v, -1.0); + v = fmin(v, 1.0); + return acos(v); +} + +Vector3 Vector3::ClampMagnitude(Vector3 vector, float maxLength) +{ + float length = Magnitude(vector); + if (length > maxLength) + vector *= maxLength / length; + return vector; +} + +float Vector3::Component(Vector3 a, Vector3 b) +{ + return Dot(a, b) / Magnitude(b); +} + +Vector3 Vector3::Cross(Vector3 lhs, Vector3 rhs) +{ + float x = lhs.Y * rhs.Z - lhs.Z * rhs.Y; + float y = lhs.Z * rhs.X - lhs.X * rhs.Z; + float z = lhs.X * rhs.Y - lhs.Y * rhs.X; + return Vector3(x, y, z); +} + +float Vector3::Distance(Vector3 a, Vector3 b) +{ + return Vector3::Magnitude(a - b); +} + +float Vector3::Dot(Vector3 lhs, Vector3 rhs) +{ + return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z; +} + +Vector3 Vector3::FromSpherical(float rad, float theta, float phi) +{ + Vector3 v; + v.X = rad * sin(theta) * cos(phi); + v.Y = rad * sin(theta) * sin(phi); + v.Z = rad * cos(theta); + return v; +} + +Vector3 Vector3::Lerp(Vector3 a, Vector3 b, float t) +{ + if (t < 0) return a; + else if (t > 1) return b; + return LerpUnclamped(a, b, t); +} + +Vector3 Vector3::LerpUnclamped(Vector3 a, Vector3 b, float t) +{ + return (b - a) * t + a; +} + +float Vector3::Magnitude(Vector3 v) +{ + return sqrt(SqrMagnitude(v)); +} + +Vector3 Vector3::Max(Vector3 a, Vector3 b) +{ + float x = a.X > b.X ? a.X : b.X; + float y = a.Y > b.Y ? a.Y : b.Y; + float z = a.Z > b.Z ? a.Z : b.Z; + return Vector3(x, y, z); +} + +Vector3 Vector3::Min(Vector3 a, Vector3 b) +{ + float x = a.X > b.X ? b.X : a.X; + float y = a.Y > b.Y ? b.Y : a.Y; + float z = a.Z > b.Z ? b.Z : a.Z; + return Vector3(x, y, z); +} + +Vector3 Vector3::MoveTowards(Vector3 current, Vector3 target, + float maxDistanceDelta) +{ + Vector3 d = target - current; + float m = Magnitude(d); + if (m < maxDistanceDelta || m == 0) + return target; + return current + (d * maxDistanceDelta / m); +} + +Vector3 Vector3::Normalized(Vector3 v) +{ + float mag = Magnitude(v); + if (mag == 0) + return Vector3::Zero(); + return v / mag; +} + +Vector3 Vector3::Orthogonal(Vector3 v) +{ + return v.Z < v.X ? Vector3(v.Y, -v.X, 0) : Vector3(0, -v.Z, v.Y); +} + +void Vector3::OrthoNormalize(Vector3 &normal, Vector3 &tangent, + Vector3 &binormal) +{ + normal = Normalized(normal); + tangent = ProjectOnPlane(tangent, normal); + tangent = Normalized(tangent); + binormal = ProjectOnPlane(binormal, tangent); + binormal = ProjectOnPlane(binormal, normal); + binormal = Normalized(binormal); +} + +Vector3 Vector3::Project(Vector3 a, Vector3 b) +{ + float m = Magnitude(b); + return Dot(a, b) / (m * m) * b; +} + +Vector3 Vector3::ProjectOnPlane(Vector3 vector, Vector3 planeNormal) +{ + return Reject(vector, planeNormal); +} + +Vector3 Vector3::Reflect(Vector3 vector, Vector3 planeNormal) +{ + return vector - 2 * Project(vector, planeNormal); +} + +Vector3 Vector3::Reject(Vector3 a, Vector3 b) +{ + return a - Project(a, b); +} + +Vector3 Vector3::RotateTowards(Vector3 current, Vector3 target, + float maxRadiansDelta, + float maxMagnitudeDelta) +{ + float magCur = Magnitude(current); + float magTar = Magnitude(target); + float newMag = magCur + maxMagnitudeDelta * + ((magTar > magCur) - (magCur > magTar)); + newMag = fmin(newMag, fmax(magCur, magTar)); + newMag = fmax(newMag, fmin(magCur, magTar)); + + float totalAngle = Angle(current, target) - maxRadiansDelta; + if (totalAngle <= 0) + return Normalized(target) * newMag; + else if (totalAngle >= M_PI) + return Normalized(-target) * newMag; + + Vector3 axis = Cross(current, target); + float magAxis = Magnitude(axis); + if (magAxis == 0) + axis = Normalized(Cross(current, current + Vector3(3.95, 5.32, -4.24))); + else + axis /= magAxis; + current = Normalized(current); + Vector3 newVector = current * cos(maxRadiansDelta) + + Cross(axis, current) * sin(maxRadiansDelta); + return newVector * newMag; +} + +Vector3 Vector3::Scale(Vector3 a, Vector3 b) +{ + return Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z); +} + +Vector3 Vector3::Slerp(Vector3 a, Vector3 b, float t) +{ + if (t < 0) return a; + else if (t > 1) return b; + return SlerpUnclamped(a, b, t); +} + +Vector3 Vector3::SlerpUnclamped(Vector3 a, Vector3 b, float t) +{ + float magA = Magnitude(a); + float magB = Magnitude(b); + a /= magA; + b /= magB; + float dot = Dot(a, b); + dot = fmax(dot, -1.0); + dot = fmin(dot, 1.0); + float theta = acos(dot) * t; + Vector3 relativeVec = Normalized(b - a * dot); + Vector3 newVec = a * cos(theta) + relativeVec * sin(theta); + return newVec * (magA + (magB - magA) * t); +} + +float Vector3::SqrMagnitude(Vector3 v) +{ + return v.X * v.X + v.Y * v.Y + v.Z * v.Z; +} + +void Vector3::ToSpherical(Vector3 vector, float &rad, float &theta, + float &phi) +{ + rad = Magnitude(vector); + float v = vector.Z / rad; + v = fmax(v, -1.0); + v = fmin(v, 1.0); + theta = acos(v); + phi = atan2(vector.Y, vector.X); +} + + +struct Vector3& Vector3::operator+=(const float rhs) +{ + X += rhs; + Y += rhs; + Z += rhs; + return *this; +} + +struct Vector3& Vector3::operator-=(const float rhs) +{ + X -= rhs; + Y -= rhs; + Z -= rhs; + return *this; +} + +struct Vector3& Vector3::operator*=(const float rhs) +{ + X *= rhs; + Y *= rhs; + Z *= rhs; + return *this; +} + +struct Vector3& Vector3::operator/=(const float rhs) +{ + X /= rhs; + Y /= rhs; + Z /= rhs; + return *this; +} + +struct Vector3& Vector3::operator+=(const Vector3 rhs) +{ + X += rhs.X; + Y += rhs.Y; + Z += rhs.Z; + return *this; +} + +struct Vector3& Vector3::operator-=(const Vector3 rhs) +{ + X -= rhs.X; + Y -= rhs.Y; + Z -= rhs.Z; + return *this; +} + +char Vector3::ToChar(Vector3 a) { + const char* x = (const char*)(int)a.X; + const char* y = (const char*)(int)a.Y; + const char* z = (const char*)(int)a.Z; + char buffer[25]; + strncpy(buffer, x, sizeof(buffer)); + strncpy(buffer, ", ", sizeof(buffer)); + strncpy(buffer, y, sizeof(buffer)); + strncpy(buffer, ", ", sizeof(buffer)); + strncpy(buffer, z, sizeof(buffer)); + strncpy(buffer, ", ", sizeof(buffer)); + return buffer[24]; +} + +Vector3 operator-(Vector3 rhs) { return rhs * -1; } +Vector3 operator+(Vector3 lhs, const float rhs) { return lhs += rhs; } +Vector3 operator-(Vector3 lhs, const float rhs) { return lhs -= rhs; } +Vector3 operator*(Vector3 lhs, const float rhs) { return lhs *= rhs; } +Vector3 operator/(Vector3 lhs, const float rhs) { return lhs /= rhs; } +Vector3 operator+(const float lhs, Vector3 rhs) { return rhs += lhs; } +Vector3 operator-(const float lhs, Vector3 rhs) { return rhs -= lhs; } +Vector3 operator*(const float lhs, Vector3 rhs) { return rhs *= lhs; } +Vector3 operator/(const float lhs, Vector3 rhs) { return rhs /= lhs; } +Vector3 operator+(Vector3 lhs, const Vector3 rhs) { return lhs += rhs; } +Vector3 operator-(Vector3 lhs, const Vector3 rhs) { return lhs -= rhs; } + +bool operator==(const Vector3 lhs, const Vector3 rhs) +{ + return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z; +} + +bool operator!=(const Vector3 lhs, const Vector3 rhs) +{ + return !(lhs == rhs); +} \ No newline at end of file diff --git a/app/src/main/jni/src/main.cpp b/app/src/main/jni/src/main.cpp index d28162c..521e12c 100644 --- a/app/src/main/jni/src/main.cpp +++ b/app/src/main/jni/src/main.cpp @@ -1001,3 +1001,9 @@ void initializer() { pthread_t ptid; pthread_create(&ptid, NULL, Super_thread, NULL); } + + + + + + diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index e69de29..971add5 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index e69de29..eed7a42 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e69de29..38dce7e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index e69de29..a26f6fb 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index e69de29..a26f6fb 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index e69de29..6c45362 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #212121 + #151515 + #0D47A1 + #202020 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e69de29..e4e1071 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Floating ModMenu + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e69de29..90260fb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/build.gradle b/build.gradle index 790efc4..73a7a42 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,27 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - google() - jcenter() - - } - dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - google() - jcenter() - - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + + } + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties index 82618ce..9f85f38 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,15 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - - +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1754716..42267b2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri May 21 10:48:45 IST 2021 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +#Wed Mar 24 11:48:11 CET 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/settings.gradle b/settings.gradle index e7b4def..d3db109 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':app'