mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-05-02 02:04:48 +12:00
FIX ELF LOADER AND OPTIMINIZE JNI-CALBACKS
This commit is contained in:
parent
36895ede35
commit
5ec6d8a5d5
13 changed files with 178 additions and 31 deletions
|
@ -15,6 +15,9 @@ RendererGL* renderer = nullptr;
|
||||||
bool romLoaded = false;
|
bool romLoaded = false;
|
||||||
JavaVM* jvm = nullptr;
|
JavaVM* jvm = nullptr;
|
||||||
|
|
||||||
|
jclass alberClass;
|
||||||
|
jmethodID alberClassOpenDocument;
|
||||||
|
|
||||||
#define AlberFunction(type, name) JNIEXPORT type JNICALL Java_com_panda3ds_pandroid_AlberDriver_##name
|
#define AlberFunction(type, name) JNIEXPORT type JNICALL Java_com_panda3ds_pandroid_AlberDriver_##name
|
||||||
|
|
||||||
void throwException(JNIEnv* env, const char* message) {
|
void throwException(JNIEnv* env, const char* message) {
|
||||||
|
@ -37,13 +40,10 @@ JNIEnv* jniEnv() {
|
||||||
int AndroidUtils::openDocument(const char* path, const char* perms) {
|
int AndroidUtils::openDocument(const char* path, const char* perms) {
|
||||||
auto env = jniEnv();
|
auto env = jniEnv();
|
||||||
|
|
||||||
auto clazz = env->FindClass("com/panda3ds/pandroid/AlberDriver");
|
|
||||||
auto method = env->GetStaticMethodID(clazz, "openDocument", "(Ljava/lang/String;Ljava/lang/String;)I");
|
|
||||||
|
|
||||||
jstring uri = env->NewStringUTF(path);
|
jstring uri = env->NewStringUTF(path);
|
||||||
jstring jmode = env->NewStringUTF(perms);
|
jstring jmode = env->NewStringUTF(perms);
|
||||||
|
|
||||||
jint result = env->CallStaticIntMethod(clazz, method, uri, jmode);
|
jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode);
|
||||||
|
|
||||||
env->DeleteLocalRef(uri);
|
env->DeleteLocalRef(uri);
|
||||||
env->DeleteLocalRef(jmode);
|
env->DeleteLocalRef(jmode);
|
||||||
|
@ -60,7 +60,13 @@ MAKE_SETTING(setShaderJitEnabled, jboolean, shaderJitEnabled)
|
||||||
|
|
||||||
#undef MAKE_SETTING
|
#undef MAKE_SETTING
|
||||||
|
|
||||||
AlberFunction(void, Setup)(JNIEnv* env, jobject obj) { env->GetJavaVM(&jvm); }
|
AlberFunction(void, Setup)(JNIEnv* env, jobject obj) {
|
||||||
|
env->GetJavaVM(&jvm);
|
||||||
|
|
||||||
|
alberClass = (jclass)env->NewGlobalRef((jclass)env->FindClass("com/panda3ds/pandroid/AlberDriver"));
|
||||||
|
alberClassOpenDocument = env->GetStaticMethodID(alberClass, "openDocument", "(Ljava/lang/String;Ljava/lang/String;)I");
|
||||||
|
}
|
||||||
|
|
||||||
AlberFunction(void, Pause)(JNIEnv* env, jobject obj) { emulator->pause(); }
|
AlberFunction(void, Pause)(JNIEnv* env, jobject obj) { emulator->pause(); }
|
||||||
AlberFunction(void, Resume)(JNIEnv* env, jobject obj) { emulator->resume(); }
|
AlberFunction(void, Resume)(JNIEnv* env, jobject obj) { emulator->resume(); }
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class AlberDriver {
|
||||||
if (mode.contains("w")){
|
if (mode.contains("w")){
|
||||||
throw new IllegalArgumentException("Cannot write to rom-fs");
|
throw new IllegalArgumentException("Cannot write to rom-fs");
|
||||||
}
|
}
|
||||||
uri = FileUtils.obtainUri(GameUtils.getCurrentGame().getRomPath());
|
uri = FileUtils.obtainUri(GameUtils.getCurrentGame().getRealPath());
|
||||||
}
|
}
|
||||||
parcel = context.getContentResolver().openFileDescriptor(uri, mode);
|
parcel = context.getContentResolver().openFileDescriptor(uri, mode);
|
||||||
int fd = parcel.detachFd();
|
int fd = parcel.detachFd();
|
||||||
|
|
|
@ -1,32 +1,20 @@
|
||||||
package com.panda3ds.pandroid.app;
|
package com.panda3ds.pandroid.app;
|
||||||
|
|
||||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
|
||||||
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
|
||||||
import static android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import com.google.android.material.navigation.NavigationBarView;
|
import com.google.android.material.navigation.NavigationBarView;
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
import com.panda3ds.pandroid.app.editor.CodeEditorActivity;
|
|
||||||
import com.panda3ds.pandroid.app.main.GamesFragment;
|
import com.panda3ds.pandroid.app.main.GamesFragment;
|
||||||
import com.panda3ds.pandroid.app.main.SearchFragment;
|
import com.panda3ds.pandroid.app.main.SearchFragment;
|
||||||
import com.panda3ds.pandroid.app.main.SettingsFragment;
|
import com.panda3ds.pandroid.app.main.SettingsFragment;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends BaseActivity implements NavigationBarView.OnItemSelectedListener {
|
public class MainActivity extends BaseActivity implements NavigationBarView.OnItemSelectedListener {
|
||||||
private static final int PICK_ROM = 2;
|
|
||||||
private static final int PERMISSION_REQUEST_CODE = 3;
|
|
||||||
|
|
||||||
private final GamesFragment gamesFragment = new GamesFragment();
|
private final GamesFragment gamesFragment = new GamesFragment();
|
||||||
private final SearchFragment searchFragment = new SearchFragment();
|
private final SearchFragment searchFragment = new SearchFragment();
|
||||||
private final SettingsFragment settingsFragment = new SettingsFragment();
|
private final SettingsFragment settingsFragment = new SettingsFragment();
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.panda3ds.pandroid.app.base;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.appcompat.widget.AppCompatTextView;
|
||||||
|
|
||||||
|
import com.panda3ds.pandroid.R;
|
||||||
|
|
||||||
|
public class LoadingAlertDialog extends BottomAlertDialog {
|
||||||
|
public LoadingAlertDialog(@NonNull Context context, @StringRes int title) {
|
||||||
|
super(context);
|
||||||
|
View view = LayoutInflater.from(context).inflate(R.layout.dialog_loading,null, false);
|
||||||
|
setView(view);
|
||||||
|
setCancelable(false);
|
||||||
|
((AppCompatTextView)view.findViewById(R.id.title))
|
||||||
|
.setText(title);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import com.panda3ds.pandroid.app.base.BottomAlertDialog;
|
||||||
import com.panda3ds.pandroid.app.base.BottomDialogFragment;
|
import com.panda3ds.pandroid.app.base.BottomDialogFragment;
|
||||||
import com.panda3ds.pandroid.app.editor.CodeEditorActivity;
|
import com.panda3ds.pandroid.app.editor.CodeEditorActivity;
|
||||||
import com.panda3ds.pandroid.lang.Task;
|
import com.panda3ds.pandroid.lang.Task;
|
||||||
|
import com.panda3ds.pandroid.utils.Constants;
|
||||||
import com.panda3ds.pandroid.utils.FileUtils;
|
import com.panda3ds.pandroid.utils.FileUtils;
|
||||||
import com.panda3ds.pandroid.view.recycler.AutoFitGridLayout;
|
import com.panda3ds.pandroid.view.recycler.AutoFitGridLayout;
|
||||||
import com.panda3ds.pandroid.view.recycler.SimpleListAdapter;
|
import com.panda3ds.pandroid.view.recycler.SimpleListAdapter;
|
||||||
|
@ -94,9 +95,8 @@ public class LuaDialogFragment extends BottomDialogFragment {
|
||||||
|
|
||||||
((RecyclerView) view.findViewById(R.id.recycler)).setAdapter(adapter);
|
((RecyclerView) view.findViewById(R.id.recycler)).setAdapter(adapter);
|
||||||
((RecyclerView) view.findViewById(R.id.recycler)).setLayoutManager(new AutoFitGridLayout(getContext(), 140));
|
((RecyclerView) view.findViewById(R.id.recycler)).setLayoutManager(new AutoFitGridLayout(getContext(), 140));
|
||||||
FileUtils.createDir(FileUtils.getResourcesPath(), "Lua Scripts");
|
|
||||||
ArrayList<LuaFile> files = new ArrayList<>();
|
ArrayList<LuaFile> files = new ArrayList<>();
|
||||||
String path = FileUtils.getResourcesPath() + "/Lua Scripts/";
|
String path = FileUtils.getResourcePath(Constants.RESOURCE_FOLDER_LUA_SCRIPTS);
|
||||||
for (String file : FileUtils.listFiles(path)) {
|
for (String file : FileUtils.listFiles(path)) {
|
||||||
files.add(new LuaFile(file));
|
files.add(new LuaFile(file));
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ public class LuaDialogFragment extends BottomDialogFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private LuaFile(String name) {
|
private LuaFile(String name) {
|
||||||
this(FileUtils.getResourcesPath() + "/Lua Scripts/", name);
|
this(FileUtils.getResourcePath(Constants.RESOURCE_FOLDER_LUA_SCRIPTS), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String absolutePath() {
|
private String absolutePath() {
|
||||||
|
|
|
@ -11,13 +11,19 @@ import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
|
import com.panda3ds.pandroid.app.base.LoadingAlertDialog;
|
||||||
import com.panda3ds.pandroid.data.game.GameMetadata;
|
import com.panda3ds.pandroid.data.game.GameMetadata;
|
||||||
|
import com.panda3ds.pandroid.lang.Task;
|
||||||
|
import com.panda3ds.pandroid.utils.Constants;
|
||||||
import com.panda3ds.pandroid.utils.FileUtils;
|
import com.panda3ds.pandroid.utils.FileUtils;
|
||||||
import com.panda3ds.pandroid.utils.GameUtils;
|
import com.panda3ds.pandroid.utils.GameUtils;
|
||||||
import com.panda3ds.pandroid.view.gamesgrid.GamesGridView;
|
import com.panda3ds.pandroid.view.gamesgrid.GamesGridView;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
public class GamesFragment extends Fragment implements ActivityResultCallback<Uri> {
|
public class GamesFragment extends Fragment implements ActivityResultCallback<Uri> {
|
||||||
private final ActivityResultContracts.OpenDocument openRomContract = new ActivityResultContracts.OpenDocument();
|
private final ActivityResultContracts.OpenDocument openRomContract = new ActivityResultContracts.OpenDocument();
|
||||||
|
@ -53,14 +59,41 @@ public class GamesFragment extends Fragment implements ActivityResultCallback<Ur
|
||||||
Toast.makeText(getContext(), "Invalid file path", Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), "Invalid file path", Toast.LENGTH_LONG).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FileUtils.makeUriPermanent(uri, FileUtils.MODE_READ);
|
|
||||||
GameMetadata game = new GameMetadata(uri, FileUtils.getName(uri).split("\\.")[0], "Unknown");
|
String extension = FileUtils.extension(uri);
|
||||||
GameUtils.addGame(game);
|
|
||||||
GameUtils.launch(requireActivity(), game);
|
if (extension.equals("elf") || extension.endsWith("axf")){
|
||||||
|
importELF(uri);
|
||||||
|
} else {
|
||||||
|
FileUtils.makeUriPermanent(uri, FileUtils.MODE_READ);
|
||||||
|
importGame(uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void importGame(String uri){
|
||||||
|
GameMetadata game = new GameMetadata(uri, FileUtils.getName(uri).split("\\.")[0], getString(R.string.unknown));
|
||||||
|
GameUtils.addGame(game);
|
||||||
|
GameUtils.launch(requireActivity(), game);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importELF(String uri) {
|
||||||
|
AlertDialog dialog = new LoadingAlertDialog(requireActivity(), R.string.loading).create();
|
||||||
|
dialog.show();
|
||||||
|
new Task(()->{
|
||||||
|
String uuid = UUID.randomUUID().toString() + "." + FileUtils.extension(uri);
|
||||||
|
String name = FileUtils.getName(uri);
|
||||||
|
FileUtils.copyFile(uri, FileUtils.getResourcePath(Constants.RESOURCE_FOLDER_ELF), uuid);
|
||||||
|
gameListView.post(()->{
|
||||||
|
dialog.hide();
|
||||||
|
GameMetadata game = new GameMetadata("elf://"+uuid, name.substring(0, name.length()-4).trim(), "");
|
||||||
|
GameUtils.addGame(game);
|
||||||
|
GameUtils.launch(requireActivity(), game);
|
||||||
|
});
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
|
@ -43,6 +43,10 @@ public class GameMetadata {
|
||||||
return romPath;
|
return romPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRealPath(){
|
||||||
|
return GameUtils.resolvePath(romPath);
|
||||||
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,4 +26,6 @@ public class Constants {
|
||||||
public static final String PREF_GAME_UTILS = "app.GameUtils";
|
public static final String PREF_GAME_UTILS = "app.GameUtils";
|
||||||
public static final String PREF_INPUT_MAP = "app.InputMap";
|
public static final String PREF_INPUT_MAP = "app.InputMap";
|
||||||
public static final String PREF_SCREEN_CONTROLLER_PROFILES = "app.input.ScreenControllerManager";
|
public static final String PREF_SCREEN_CONTROLLER_PROFILES = "app.input.ScreenControllerManager";
|
||||||
|
public static final String RESOURCE_FOLDER_ELF = "ELF";
|
||||||
|
public static final String RESOURCE_FOLDER_LUA_SCRIPTS = "Lua Scripts";
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,13 @@ public class FileUtils {
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getResourcePath(String name){
|
||||||
|
File file = new File(getResourcesPath(), name);
|
||||||
|
file.mkdirs();
|
||||||
|
|
||||||
|
return file.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
public static String getPrivatePath() {
|
public static String getPrivatePath() {
|
||||||
File file = getContext().getFilesDir();
|
File file = getContext().getFilesDir();
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
|
@ -268,4 +275,37 @@ public class FileUtils {
|
||||||
public static Uri obtainUri(String path) {
|
public static Uri obtainUri(String path) {
|
||||||
return parseFile(path).getUri();
|
return parseFile(path).getUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String extension(String uri) {
|
||||||
|
String name = getName(uri);
|
||||||
|
if (!name.contains(".")){
|
||||||
|
return name.toLowerCase();
|
||||||
|
}
|
||||||
|
String[] parts = name.split("\\.");
|
||||||
|
return parts[parts.length-1].toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copyFile(String source, String path, String name) {
|
||||||
|
try {
|
||||||
|
String fullPath = path + "/" + name;
|
||||||
|
if (!FileUtils.exists(fullPath)) {
|
||||||
|
FileUtils.delete(fullPath);
|
||||||
|
}
|
||||||
|
FileUtils.createFile(path, name);
|
||||||
|
InputStream in = getInputStream(source);
|
||||||
|
OutputStream out = getOutputStream(fullPath);
|
||||||
|
byte[] buffer = new byte[1024 * 128]; //128 KB
|
||||||
|
int length;
|
||||||
|
while ((length = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
in.close();
|
||||||
|
} catch (Exception e){
|
||||||
|
Log.e(Constants.LOG_TAG, "ERROR ON COPY FILE", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class GameUtils {
|
||||||
|
|
||||||
public static GameMetadata findByRomPath(String romPath) {
|
public static GameMetadata findByRomPath(String romPath) {
|
||||||
for (GameMetadata game : data.games) {
|
for (GameMetadata game : data.games) {
|
||||||
if (Objects.equals(romPath, game.getRomPath())) {
|
if (Objects.equals(romPath, game.getRealPath())) {
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,12 @@ public class GameUtils {
|
||||||
|
|
||||||
public static void launch(Context context, GameMetadata game) {
|
public static void launch(Context context, GameMetadata game) {
|
||||||
currentGame = game;
|
currentGame = game;
|
||||||
String[] parts = Uri.decode(game.getRomPath()).split("/");
|
String path = game.getRealPath();
|
||||||
String name = parts[parts.length-1];
|
if (path.contains("://")) {
|
||||||
String path = "game://internal/"+name;
|
String[] parts = Uri.decode(game.getRomPath()).split("/");
|
||||||
|
String name = parts[parts.length - 1];
|
||||||
|
path = "game://internal/" + name;
|
||||||
|
}
|
||||||
context.startActivity(new Intent(context, GameActivity.class).putExtra(Constants.ACTIVITY_PARAMETER_PATH, path));
|
context.startActivity(new Intent(context, GameActivity.class).putExtra(Constants.ACTIVITY_PARAMETER_PATH, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +63,20 @@ public class GameUtils {
|
||||||
writeChanges();
|
writeChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String resolvePath(String path){
|
||||||
|
String lower = path.toLowerCase();
|
||||||
|
if (!lower.contains("://")){
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse(path);
|
||||||
|
switch (uri.getScheme().toLowerCase()){
|
||||||
|
case "elf":{
|
||||||
|
return FileUtils.getResourcePath(Constants.RESOURCE_FOLDER_ELF)+"/"+uri.getAuthority();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
public static ArrayList<GameMetadata> getGames() {
|
public static ArrayList<GameMetadata> getGames() {
|
||||||
return new ArrayList<>(data.games);
|
return new ArrayList<>(data.games);
|
||||||
}
|
}
|
||||||
|
|
33
src/pandroid/app/src/main/res/layout/dialog_loading.xml
Normal file
33
src/pandroid/app/src/main/res/layout/dialog_loading.xml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface"
|
||||||
|
android:padding="30dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="20dp">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="24sp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:indeterminateTint="?colorPrimary"
|
||||||
|
android:background="#0000"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
|
@ -54,5 +54,6 @@
|
||||||
<string name="pref_shader_jit_title">Shader Jit</string>
|
<string name="pref_shader_jit_title">Shader Jit</string>
|
||||||
<string name="pref_shader_jit_summary">Usar recompilador de shaders.</string>
|
<string name="pref_shader_jit_summary">Usar recompilador de shaders.</string>
|
||||||
<string name="graphics">Gráficos</string>
|
<string name="graphics">Gráficos</string>
|
||||||
|
<string name="loading">Carregando</string>
|
||||||
<string name="rotate">Rotacionar</string>
|
<string name="rotate">Rotacionar</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -59,4 +59,5 @@
|
||||||
<string name="pref_shader_jit_title">Shader JIT</string>
|
<string name="pref_shader_jit_title">Shader JIT</string>
|
||||||
<string name="pref_shader_jit_summary">Use shader recompiler.</string>
|
<string name="pref_shader_jit_summary">Use shader recompiler.</string>
|
||||||
<string name="graphics">Graphics</string>
|
<string name="graphics">Graphics</string>
|
||||||
|
<string name="loading">Loading</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue