1 /*
2  * Copyright 2013 Dolphin Emulator Project
3  * Licensed under GPLv2+
4  * Refer to the license.txt file included.
5  */
6 
7 package org.dolphinemu.dolphinemu;
8 
9 import android.util.DisplayMetrics;
10 import android.view.Surface;
11 
12 import androidx.appcompat.app.AlertDialog;
13 
14 import org.dolphinemu.dolphinemu.activities.EmulationActivity;
15 import org.dolphinemu.dolphinemu.utils.CompressCallback;
16 import org.dolphinemu.dolphinemu.utils.Log;
17 import org.dolphinemu.dolphinemu.utils.Rumble;
18 
19 import java.lang.ref.WeakReference;
20 import java.util.LinkedHashMap;
21 
22 /**
23  * Class which contains methods that interact
24  * with the native side of the Dolphin code.
25  */
26 public final class NativeLibrary
27 {
28   private static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
29 
30   /**
31    * Returns the current instance of EmulationActivity.
32    * There should only ever be one EmulationActivity instantiated.
33    */
getEmulationActivity()34   public static EmulationActivity getEmulationActivity()
35   {
36     return sEmulationActivity.get();
37   }
38 
39   /**
40    * Button type for use in onTouchEvent
41    */
42   public static final class ButtonType
43   {
44     public static final int BUTTON_A = 0;
45     public static final int BUTTON_B = 1;
46     public static final int BUTTON_START = 2;
47     public static final int BUTTON_X = 3;
48     public static final int BUTTON_Y = 4;
49     public static final int BUTTON_Z = 5;
50     public static final int BUTTON_UP = 6;
51     public static final int BUTTON_DOWN = 7;
52     public static final int BUTTON_LEFT = 8;
53     public static final int BUTTON_RIGHT = 9;
54     public static final int STICK_MAIN = 10;
55     public static final int STICK_MAIN_UP = 11;
56     public static final int STICK_MAIN_DOWN = 12;
57     public static final int STICK_MAIN_LEFT = 13;
58     public static final int STICK_MAIN_RIGHT = 14;
59     public static final int STICK_C = 15;
60     public static final int STICK_C_UP = 16;
61     public static final int STICK_C_DOWN = 17;
62     public static final int STICK_C_LEFT = 18;
63     public static final int STICK_C_RIGHT = 19;
64     public static final int TRIGGER_L = 20;
65     public static final int TRIGGER_R = 21;
66     public static final int WIIMOTE_BUTTON_A = 100;
67     public static final int WIIMOTE_BUTTON_B = 101;
68     public static final int WIIMOTE_BUTTON_MINUS = 102;
69     public static final int WIIMOTE_BUTTON_PLUS = 103;
70     public static final int WIIMOTE_BUTTON_HOME = 104;
71     public static final int WIIMOTE_BUTTON_1 = 105;
72     public static final int WIIMOTE_BUTTON_2 = 106;
73     public static final int WIIMOTE_UP = 107;
74     public static final int WIIMOTE_DOWN = 108;
75     public static final int WIIMOTE_LEFT = 109;
76     public static final int WIIMOTE_RIGHT = 110;
77     public static final int WIIMOTE_IR = 111;
78     public static final int WIIMOTE_IR_UP = 112;
79     public static final int WIIMOTE_IR_DOWN = 113;
80     public static final int WIIMOTE_IR_LEFT = 114;
81     public static final int WIIMOTE_IR_RIGHT = 115;
82     public static final int WIIMOTE_IR_FORWARD = 116;
83     public static final int WIIMOTE_IR_BACKWARD = 117;
84     public static final int WIIMOTE_IR_HIDE = 118;
85     public static final int WIIMOTE_SWING = 119;
86     public static final int WIIMOTE_SWING_UP = 120;
87     public static final int WIIMOTE_SWING_DOWN = 121;
88     public static final int WIIMOTE_SWING_LEFT = 122;
89     public static final int WIIMOTE_SWING_RIGHT = 123;
90     public static final int WIIMOTE_SWING_FORWARD = 124;
91     public static final int WIIMOTE_SWING_BACKWARD = 125;
92     public static final int WIIMOTE_TILT = 126;
93     public static final int WIIMOTE_TILT_FORWARD = 127;
94     public static final int WIIMOTE_TILT_BACKWARD = 128;
95     public static final int WIIMOTE_TILT_LEFT = 129;
96     public static final int WIIMOTE_TILT_RIGHT = 130;
97     public static final int WIIMOTE_TILT_MODIFIER = 131;
98     public static final int WIIMOTE_SHAKE_X = 132;
99     public static final int WIIMOTE_SHAKE_Y = 133;
100     public static final int WIIMOTE_SHAKE_Z = 134;
101     public static final int NUNCHUK_BUTTON_C = 200;
102     public static final int NUNCHUK_BUTTON_Z = 201;
103     public static final int NUNCHUK_STICK = 202;
104     public static final int NUNCHUK_STICK_UP = 203;
105     public static final int NUNCHUK_STICK_DOWN = 204;
106     public static final int NUNCHUK_STICK_LEFT = 205;
107     public static final int NUNCHUK_STICK_RIGHT = 206;
108     public static final int NUNCHUK_SWING = 207;
109     public static final int NUNCHUK_SWING_UP = 208;
110     public static final int NUNCHUK_SWING_DOWN = 209;
111     public static final int NUNCHUK_SWING_LEFT = 210;
112     public static final int NUNCHUK_SWING_RIGHT = 221;
113     public static final int NUNCHUK_SWING_FORWARD = 212;
114     public static final int NUNCHUK_SWING_BACKWARD = 213;
115     public static final int NUNCHUK_TILT = 214;
116     public static final int NUNCHUK_TILT_FORWARD = 215;
117     public static final int NUNCHUK_TILT_BACKWARD = 216;
118     public static final int NUNCHUK_TILT_LEFT = 217;
119     public static final int NUNCHUK_TILT_RIGHT = 218;
120     public static final int NUNCHUK_TILT_MODIFIER = 219;
121     public static final int NUNCHUK_SHAKE_X = 220;
122     public static final int NUNCHUK_SHAKE_Y = 221;
123     public static final int NUNCHUK_SHAKE_Z = 222;
124     public static final int CLASSIC_BUTTON_A = 300;
125     public static final int CLASSIC_BUTTON_B = 301;
126     public static final int CLASSIC_BUTTON_X = 302;
127     public static final int CLASSIC_BUTTON_Y = 303;
128     public static final int CLASSIC_BUTTON_MINUS = 304;
129     public static final int CLASSIC_BUTTON_PLUS = 305;
130     public static final int CLASSIC_BUTTON_HOME = 306;
131     public static final int CLASSIC_BUTTON_ZL = 307;
132     public static final int CLASSIC_BUTTON_ZR = 308;
133     public static final int CLASSIC_DPAD_UP = 309;
134     public static final int CLASSIC_DPAD_DOWN = 310;
135     public static final int CLASSIC_DPAD_LEFT = 311;
136     public static final int CLASSIC_DPAD_RIGHT = 312;
137     public static final int CLASSIC_STICK_LEFT = 313;
138     public static final int CLASSIC_STICK_LEFT_UP = 314;
139     public static final int CLASSIC_STICK_LEFT_DOWN = 315;
140     public static final int CLASSIC_STICK_LEFT_LEFT = 316;
141     public static final int CLASSIC_STICK_LEFT_RIGHT = 317;
142     public static final int CLASSIC_STICK_RIGHT = 318;
143     public static final int CLASSIC_STICK_RIGHT_UP = 319;
144     public static final int CLASSIC_STICK_RIGHT_DOWN = 320;
145     public static final int CLASSIC_STICK_RIGHT_LEFT = 321;
146     public static final int CLASSIC_STICK_RIGHT_RIGHT = 322;
147     public static final int CLASSIC_TRIGGER_L = 323;
148     public static final int CLASSIC_TRIGGER_R = 324;
149     public static final int GUITAR_BUTTON_MINUS = 400;
150     public static final int GUITAR_BUTTON_PLUS = 401;
151     public static final int GUITAR_FRET_GREEN = 402;
152     public static final int GUITAR_FRET_RED = 403;
153     public static final int GUITAR_FRET_YELLOW = 404;
154     public static final int GUITAR_FRET_BLUE = 405;
155     public static final int GUITAR_FRET_ORANGE = 406;
156     public static final int GUITAR_STRUM_UP = 407;
157     public static final int GUITAR_STRUM_DOWN = 408;
158     public static final int GUITAR_STICK = 409;
159     public static final int GUITAR_STICK_UP = 410;
160     public static final int GUITAR_STICK_DOWN = 411;
161     public static final int GUITAR_STICK_LEFT = 412;
162     public static final int GUITAR_STICK_RIGHT = 413;
163     public static final int GUITAR_WHAMMY_BAR = 414;
164     public static final int DRUMS_BUTTON_MINUS = 500;
165     public static final int DRUMS_BUTTON_PLUS = 501;
166     public static final int DRUMS_PAD_RED = 502;
167     public static final int DRUMS_PAD_YELLOW = 503;
168     public static final int DRUMS_PAD_BLUE = 504;
169     public static final int DRUMS_PAD_GREEN = 505;
170     public static final int DRUMS_PAD_ORANGE = 506;
171     public static final int DRUMS_PAD_BASS = 507;
172     public static final int DRUMS_STICK = 508;
173     public static final int DRUMS_STICK_UP = 509;
174     public static final int DRUMS_STICK_DOWN = 510;
175     public static final int DRUMS_STICK_LEFT = 511;
176     public static final int DRUMS_STICK_RIGHT = 512;
177     public static final int TURNTABLE_BUTTON_GREEN_LEFT = 600;
178     public static final int TURNTABLE_BUTTON_RED_LEFT = 601;
179     public static final int TURNTABLE_BUTTON_BLUE_LEFT = 602;
180     public static final int TURNTABLE_BUTTON_GREEN_RIGHT = 603;
181     public static final int TURNTABLE_BUTTON_RED_RIGHT = 604;
182     public static final int TURNTABLE_BUTTON_BLUE_RIGHT = 605;
183     public static final int TURNTABLE_BUTTON_MINUS = 606;
184     public static final int TURNTABLE_BUTTON_PLUS = 607;
185     public static final int TURNTABLE_BUTTON_HOME = 608;
186     public static final int TURNTABLE_BUTTON_EUPHORIA = 609;
187     public static final int TURNTABLE_TABLE_LEFT = 610;
188     public static final int TURNTABLE_TABLE_LEFT_LEFT = 611;
189     public static final int TURNTABLE_TABLE_LEFT_RIGHT = 612;
190     public static final int TURNTABLE_TABLE_RIGHT = 613;
191     public static final int TURNTABLE_TABLE_RIGHT_LEFT = 614;
192     public static final int TURNTABLE_TABLE_RIGHT_RIGHT = 615;
193     public static final int TURNTABLE_STICK = 616;
194     public static final int TURNTABLE_STICK_UP = 617;
195     public static final int TURNTABLE_STICK_DOWN = 618;
196     public static final int TURNTABLE_STICK_LEFT = 619;
197     public static final int TURNTABLE_STICK_RIGHT = 620;
198     public static final int TURNTABLE_EFFECT_DIAL = 621;
199     public static final int TURNTABLE_CROSSFADE = 622;
200     public static final int TURNTABLE_CROSSFADE_LEFT = 623;
201     public static final int TURNTABLE_CROSSFADE_RIGHT = 624;
202     public static final int WIIMOTE_ACCEL_LEFT = 625;
203     public static final int WIIMOTE_ACCEL_RIGHT = 626;
204     public static final int WIIMOTE_ACCEL_FORWARD = 627;
205     public static final int WIIMOTE_ACCEL_BACKWARD = 628;
206     public static final int WIIMOTE_ACCEL_UP = 629;
207     public static final int WIIMOTE_ACCEL_DOWN = 630;
208     public static final int WIIMOTE_GYRO_PITCH_UP = 631;
209     public static final int WIIMOTE_GYRO_PITCH_DOWN = 632;
210     public static final int WIIMOTE_GYRO_ROLL_LEFT = 633;
211     public static final int WIIMOTE_GYRO_ROLL_RIGHT = 634;
212     public static final int WIIMOTE_GYRO_YAW_LEFT = 635;
213     public static final int WIIMOTE_GYRO_YAW_RIGHT = 636;
214   }
215 
216   /**
217    * Button states
218    */
219   public static final class ButtonState
220   {
221     public static final int RELEASED = 0;
222     public static final int PRESSED = 1;
223   }
224 
NativeLibrary()225   private NativeLibrary()
226   {
227     // Disallows instantiation.
228   }
229 
230   /**
231    * Default touchscreen device
232    */
233   public static final String TouchScreenDevice = "Touchscreen";
234 
235   /**
236    * Handles button press events for a gamepad.
237    *
238    * @param Device The input descriptor of the gamepad.
239    * @param Button Key code identifying which button was pressed.
240    * @param Action Mask identifying which action is happening (button pressed down, or button released).
241    * @return If we handled the button press.
242    */
onGamePadEvent(String Device, int Button, int Action)243   public static native boolean onGamePadEvent(String Device, int Button, int Action);
244 
245   /**
246    * Handles gamepad movement events.
247    *
248    * @param Device The device ID of the gamepad.
249    * @param Axis   The axis ID
250    * @param Value  The value of the axis represented by the given ID.
251    */
onGamePadMoveEvent(String Device, int Axis, float Value)252   public static native void onGamePadMoveEvent(String Device, int Axis, float Value);
253 
254   /**
255    * Rumble sent from native. Currently only supports phone rumble.
256    *
257    * @param padID Ignored for now. Future use would be to pass rumble to a connected controller
258    * @param state Ignored for now since phone rumble can't just be 'turned' on/off
259    */
rumble(int padID, double state)260   public static void rumble(int padID, double state)
261   {
262     final EmulationActivity emulationActivity = sEmulationActivity.get();
263     if (emulationActivity == null)
264     {
265       Log.warning("[NativeLibrary] EmulationActivity is null");
266       return;
267     }
268 
269     Rumble.checkRumble(padID, state);
270   }
271 
SetMotionSensorsEnabled(boolean accelerometerEnabled, boolean gyroscopeEnabled)272   public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled,
273           boolean gyroscopeEnabled);
274 
275   // Angle is in radians and should be non-negative
GetInputRadiusAtAngle(int emu_pad_id, int stick, double angle)276   public static native double GetInputRadiusAtAngle(int emu_pad_id, int stick, double angle);
277 
278   /**
279    * Gets the Dolphin version string.
280    *
281    * @return the Dolphin version string.
282    */
GetVersionString()283   public static native String GetVersionString();
284 
GetGitRevision()285   public static native String GetGitRevision();
286 
287   /**
288    * Saves a screen capture of the game
289    */
SaveScreenShot()290   public static native void SaveScreenShot();
291 
292   /**
293    * Saves a game state to the slot number.
294    *
295    * @param slot The slot location to save state to.
296    * @param wait If false, returns as early as possible.
297    *             If true, returns once the savestate has been written to disk.
298    */
SaveState(int slot, boolean wait)299   public static native void SaveState(int slot, boolean wait);
300 
301   /**
302    * Saves a game state to the specified path.
303    *
304    * @param path The path to save state to.
305    * @param wait If false, returns as early as possible.
306    *             If true, returns once the savestate has been written to disk.
307    */
SaveStateAs(String path, boolean wait)308   public static native void SaveStateAs(String path, boolean wait);
309 
310   /**
311    * Loads a game state from the slot number.
312    *
313    * @param slot The slot location to load state from.
314    */
LoadState(int slot)315   public static native void LoadState(int slot);
316 
317   /**
318    * Loads a game state from the specified path.
319    *
320    * @param path The path to load state from.
321    */
LoadStateAs(String path)322   public static native void LoadStateAs(String path);
323 
324   /**
325    * Sets the current working user directory
326    * If not set, it auto-detects a location
327    */
SetUserDirectory(String directory)328   public static native void SetUserDirectory(String directory);
329 
330   /**
331    * Returns the current working user directory
332    */
GetUserDirectory()333   public static native String GetUserDirectory();
334 
SetCacheDirectory(String directory)335   public static native void SetCacheDirectory(String directory);
336 
DefaultCPUCore()337   public static native int DefaultCPUCore();
338 
GetDefaultGraphicsBackendName()339   public static native String GetDefaultGraphicsBackendName();
340 
GetMaxLogLevel()341   public static native int GetMaxLogLevel();
342 
ReloadConfig()343   public static native void ReloadConfig();
344 
UpdateGCAdapterScanThread()345   public static native void UpdateGCAdapterScanThread();
346 
347   /**
348    * Initializes the native parts of the app.
349    *
350    * Should be called at app start before running any other native code
351    * (other than the native methods in DirectoryInitialization).
352    */
Initialize()353   public static native void Initialize();
354 
355   /**
356    * Tells analytics that Dolphin has been started.
357    *
358    * Since users typically don't explicitly close Android apps, it's appropriate to
359    * call this not only when the app starts but also when the user returns to the app
360    * after not using it for a significant amount of time.
361    */
ReportStartToAnalytics()362   public static native void ReportStartToAnalytics();
363 
364   /**
365    * Begins emulation.
366    */
Run(String[] path)367   public static native void Run(String[] path);
368 
369   /**
370    * Begins emulation from the specified savestate.
371    */
Run(String[] path, String savestatePath, boolean deleteSavestate)372   public static native void Run(String[] path, String savestatePath, boolean deleteSavestate);
373 
ChangeDisc(String path)374   public static native void ChangeDisc(String path);
375 
376   // Surface Handling
SurfaceChanged(Surface surf)377   public static native void SurfaceChanged(Surface surf);
378 
SurfaceDestroyed()379   public static native void SurfaceDestroyed();
380 
381   /**
382    * Unpauses emulation from a paused state.
383    */
UnPauseEmulation()384   public static native void UnPauseEmulation();
385 
386   /**
387    * Pauses emulation.
388    */
PauseEmulation()389   public static native void PauseEmulation();
390 
391   /**
392    * Stops emulation.
393    */
StopEmulation()394   public static native void StopEmulation();
395 
WaitUntilDoneBooting()396   public static native void WaitUntilDoneBooting();
397 
398   /**
399    * Returns true if emulation is running (or is paused).
400    */
IsRunning()401   public static native boolean IsRunning();
402 
403   /**
404    * Enables or disables CPU block profiling
405    *
406    * @param enable
407    */
SetProfiling(boolean enable)408   public static native void SetProfiling(boolean enable);
409 
410   /**
411    * Writes out the block profile results
412    */
WriteProfileResults()413   public static native void WriteProfileResults();
414 
415   /**
416    * Native EGL functions not exposed by Java bindings
417    **/
eglBindAPI(int api)418   public static native void eglBindAPI(int api);
419 
420   /**
421    * Provides a way to refresh the connections on Wiimotes
422    */
RefreshWiimotes()423   public static native void RefreshWiimotes();
424 
ReloadWiimoteConfig()425   public static native void ReloadWiimoteConfig();
426 
GetLogTypeNames()427   public static native LinkedHashMap<String, String> GetLogTypeNames();
428 
ReloadLoggerConfig()429   public static native void ReloadLoggerConfig();
430 
InstallWAD(String file)431   public static native boolean InstallWAD(String file);
432 
ConvertDiscImage(String inPath, String outPath, int platform, int format, int blockSize, int compression, int compressionLevel, boolean scrub, CompressCallback callback)433   public static native boolean ConvertDiscImage(String inPath, String outPath, int platform,
434           int format, int blockSize, int compression, int compressionLevel, boolean scrub,
435           CompressCallback callback);
436 
FormatSize(long bytes, int decimals)437   public static native String FormatSize(long bytes, int decimals);
438 
SetObscuredPixelsLeft(int width)439   public static native void SetObscuredPixelsLeft(int width);
440 
SetObscuredPixelsTop(int height)441   public static native void SetObscuredPixelsTop(int height);
442 
443   private static boolean alertResult = false;
444 
displayAlertMsg(final String caption, final String text, final boolean yesNo)445   public static boolean displayAlertMsg(final String caption, final String text,
446           final boolean yesNo)
447   {
448     Log.error("[NativeLibrary] Alert: " + text);
449     final EmulationActivity emulationActivity = sEmulationActivity.get();
450     boolean result = false;
451     if (emulationActivity == null)
452     {
453       Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic alert.");
454     }
455     else
456     {
457       // Create object used for waiting.
458       final Object lock = new Object();
459       AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity,
460               R.style.DolphinDialogBase)
461               .setTitle(caption)
462               .setMessage(text);
463 
464       // If not yes/no dialog just have one button that dismisses modal,
465       // otherwise have a yes and no button that sets alertResult accordingly.
466       if (!yesNo)
467       {
468         builder
469                 .setCancelable(false)
470                 .setPositiveButton("OK", (dialog, whichButton) ->
471                 {
472                   dialog.dismiss();
473                   synchronized (lock)
474                   {
475                     lock.notify();
476                   }
477                 });
478       }
479       else
480       {
481         alertResult = false;
482 
483         builder
484                 .setPositiveButton("Yes", (dialog, whichButton) ->
485                 {
486                   alertResult = true;
487                   dialog.dismiss();
488                   synchronized (lock)
489                   {
490                     lock.notify();
491                   }
492                 })
493                 .setNegativeButton("No", (dialog, whichButton) ->
494                 {
495                   alertResult = false;
496                   dialog.dismiss();
497                   synchronized (lock)
498                   {
499                     lock.notify();
500                   }
501                 });
502       }
503 
504       // Show the AlertDialog on the main thread.
505       emulationActivity.runOnUiThread(builder::show);
506 
507       // Wait for the lock to notify that it is complete.
508       synchronized (lock)
509       {
510         try
511         {
512           lock.wait();
513         }
514         catch (Exception ignored)
515         {
516         }
517       }
518 
519       if (yesNo)
520         result = alertResult;
521     }
522     return result;
523   }
524 
setEmulationActivity(EmulationActivity emulationActivity)525   public static void setEmulationActivity(EmulationActivity emulationActivity)
526   {
527     Log.verbose("[NativeLibrary] Registering EmulationActivity.");
528     sEmulationActivity = new WeakReference<>(emulationActivity);
529   }
530 
clearEmulationActivity()531   public static void clearEmulationActivity()
532   {
533     Log.verbose("[NativeLibrary] Unregistering EmulationActivity.");
534 
535     sEmulationActivity.clear();
536   }
537 
updateTouchPointer()538   public static void updateTouchPointer()
539   {
540     final EmulationActivity emulationActivity = sEmulationActivity.get();
541     if (emulationActivity == null)
542     {
543       Log.warning("[NativeLibrary] EmulationActivity is null.");
544     }
545     else
546     {
547       emulationActivity.runOnUiThread(emulationActivity::initInputPointer);
548     }
549   }
550 
getRenderSurfaceScale()551   public static float getRenderSurfaceScale()
552   {
553     DisplayMetrics metrics = new DisplayMetrics();
554     sEmulationActivity.get().getWindowManager().getDefaultDisplay().getMetrics(metrics);
555     return metrics.scaledDensity;
556   }
557 
GetGameAspectRatio()558   public static native float GetGameAspectRatio();
559 }
560