From 50733fa22a914074cd107eaa6fd78d10cf221e18 Mon Sep 17 00:00:00 2001 From: ewt45 <79033456+ewt45@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:06:43 +0800 Subject: [PATCH] v0.0.4 --- app/src/main/AndroidManifest.xml | 33 +- .../mt/file/content/MTDataFilesProvider.java | 399 ++++++++++++++++++ app/src/main/java/com/eltechs/axs/Mcat.java | 7 + .../fragments/ContainerSettingsFragment.java | 31 +- .../exagear/FAB/dialogfragment/AboutFab.java | 46 +- .../com/example/datainsert/exagear/QH.java | 58 +++ .../com/example/datainsert/exagear/RR.java | 12 +- .../action/AddEnvironmentVariables.java | 18 +- .../containerSettings/ConSetRenderer.java | 5 + .../containerSettings/ConSetResolution.java | 9 + .../datainsert/exagear/mutiWine/MutiWine.java | 28 +- .../exagear/mutiWine/WineVersion.java | 3 +- .../exagear/shortcut/MoreShortcut.java | 17 + app/src/main/res/xml/file_paths.xml | 23 + patchapp/build.gradle | 4 +- patchapp/release/output-metadata.json | 4 +- .../com/ewt45/patchapp/ActivityPatch.java | 2 + .../java/com/ewt45/patchapp/PatchUtils.java | 2 +- .../fragment/FragmentChoosePatch.java | 2 + .../ewt45/patchapp/patching/PatcherFile.java | 155 +++++-- .../ewt45/patchapp/patching/SmaliFile.java | 4 + .../com/ewt45/patchapp/thread/FuncFAB.java | 12 +- .../ewt45/patchapp/thread/FuncRenderer.java | 37 ++ .../com/ewt45/patchapp/thread/FuncResl.java | 14 +- .../main/res/layout/fragment_choose_patch.xml | 5 + patchapp/src/main/res/values-ru/strings.xml | 3 +- patchapp/src/main/res/values-zh/strings.xml | 3 +- patchapp/src/main/res/values/strings.xml | 3 +- readme.md | 27 +- readme/readme_en.md | 24 +- readme/readme_ru.md | 23 +- 31 files changed, 895 insertions(+), 118 deletions(-) create mode 100644 app/src/main/java/bin/mt/file/content/MTDataFilesProvider.java create mode 100644 app/src/main/java/com/eltechs/axs/Mcat.java create mode 100644 app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java create mode 100644 app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetResolution.java create mode 100644 app/src/main/res/xml/file_paths.xml create mode 100644 patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 01f24c37..ad5e46c2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -83,15 +83,30 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/bin/mt/file/content/MTDataFilesProvider.java b/app/src/main/java/bin/mt/file/content/MTDataFilesProvider.java new file mode 100644 index 00000000..20463e5b --- /dev/null +++ b/app/src/main/java/bin/mt/file/content/MTDataFilesProvider.java @@ -0,0 +1,399 @@ +//package bin.mt.file.content; +// +//import android.content.Context; +//import android.content.pm.ApplicationInfo; +//import android.content.pm.ProviderInfo; +//import android.database.Cursor; +//import android.database.MatrixCursor; +//import android.net.Uri; +//import android.os.Bundle; +//import android.os.CancellationSignal; +//import android.os.Environment; +//import android.os.ParcelFileDescriptor; +//import android.provider.DocumentsProvider; +//import android.system.ErrnoException; +//import android.system.Os; +//import android.system.StructStat; +//import android.webkit.MimeTypeMap; +//import java.io.File; +//import java.io.FileNotFoundException; +//import java.io.IOException; +//import java.util.List; +// +//public class MTDataFilesProvider extends DocumentsProvider { +// public static final String[] f = {"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"}; +// public static final String[] g = {"document_id", "mime_type", "_display_name", "last_modified", "flags", "_size", "mt_extras"}; +// public String b; +// public File c; +// public File d; +// public File e; +// +// /* JADX WARN: Removed duplicated region for block: B:17:0x002f */ +// /* +// Code decompiled incorrectly, please refer to instructions dump. +// */ +// public static boolean a(File file) { +// boolean z; +// File[] listFiles; +// if (file.isDirectory()) { +// try { +// z = (Os.lstat(file.getPath()).st_mode & 61440) == 4096; +// } catch (ErrnoException e) { +// e.printStackTrace(); +// z = false; +// } +// listFiles = file.listFiles(); +// if (!z && listFiles!=null) { +// for (File file2 : listFiles) { +// if (!a(file2)) { +// return false; +// } +// } +// } +// +// } +// return file.delete(); +// } +// +// public static String c(File file) { +// if (file.isDirectory()) { +// return "vnd.android.document/directory"; +// } +// String name = file.getName(); +// int lastIndexOf = name.lastIndexOf(46); +// if (lastIndexOf >= 0) { +// String mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(name.substring(lastIndexOf + 1).toLowerCase()); +// return mimeTypeFromExtension != null ? mimeTypeFromExtension : "application/octet-stream"; +// } +// return "application/octet-stream"; +// } +// +// @Override // android.provider.DocumentsProvider, android.content.ContentProvider +// public final void attachInfo(Context context, ProviderInfo providerInfo) { +// super.attachInfo(context, providerInfo); +// this.b = context.getPackageName(); +// this.c = context.getFilesDir().getParentFile(); +// File externalStorageDirectory = Environment.getExternalStorageDirectory(); +// this.d = new File(externalStorageDirectory, "Android/data/" + this.b); +// File externalStorageDirectory2 = Environment.getExternalStorageDirectory(); +// this.e = new File(externalStorageDirectory2, "Android/obb/" + this.b); +// } +// +// public final File b(String str, boolean z) throws FileNotFoundException { +// String substring; +// if (str.startsWith(this.b)) { +// String substring2 = str.substring(this.b.length()); +// if (substring2.startsWith("/")) { +// substring2 = substring2.substring(1); +// } +// File file = null; +// if (substring2.isEmpty()) { +// return null; +// } +// int indexOf = substring2.indexOf(47); +// if (indexOf == -1) { +// substring = ""; +// } else { +// String substring3 = substring2.substring(0, indexOf); +// substring = substring2.substring(indexOf + 1); +// substring2 = substring3; +// } +// if (substring2.equalsIgnoreCase("data")) { +// file = new File(this.c, substring); +// } else if (substring2.equalsIgnoreCase("android_data")) { +// file = new File(this.d, substring); +// } else if (substring2.equalsIgnoreCase("android_obb")) { +// file = new File(this.e, substring); +// } +// if (file == null || (z && !file.exists())) { +// throw new FileNotFoundException(str.concat(" not found")); +// } +// return file; +// } +// throw new FileNotFoundException(str.concat(" not found")); +// } +// +// /* JADX WARN: Removed duplicated region for block: B:33:0x0074 */ +// /* JADX WARN: Removed duplicated region for block: B:53:0x00c8 A[Catch: Exception -> 0x00dd, TryCatch #1 {Exception -> 0x00dd, blocks: (B:10:0x001d, B:12:0x0031, B:13:0x0036, B:15:0x003e, B:35:0x0078, B:36:0x007f, B:37:0x0083, B:39:0x0089, B:40:0x008d, B:41:0x0093, B:44:0x009f, B:45:0x00a7, B:48:0x00ae, B:49:0x00b4, B:52:0x00c0, B:53:0x00c8, B:56:0x00cf, B:22:0x0053, B:25:0x005d, B:28:0x0067, B:14:0x0039), top: B:63:0x001d, inners: #0, #2 }] */ +// @Override // android.provider.DocumentsProvider, android.content.ContentProvider +// /* +// Code decompiled incorrectly, please refer to instructions dump. +// */ +// public final Bundle call(String str, String str2, Bundle bundle) { +// String str3; +// int hashCode = 0; +// char c; +// String message = null; +// Bundle call = super.call(str, str2, bundle); +// if (call != null) { +// return call; +// } +// if (str.startsWith("mt:")) { +// Bundle bundle2 = new Bundle(); +// try { +// List pathSegments = ((Uri) bundle.getParcelable("uri")).getPathSegments(); +// str3 = pathSegments.size() >= 4 ? pathSegments.get(3) : pathSegments.get(1); +// hashCode = str.hashCode(); +// } catch (Exception e) { +// bundle2.putBoolean("result", false); +// bundle2.putString("message", e.toString()); +// } +// if (hashCode == -1645162251) { +// if (str.equals("mt:setPermissions")) { +// c = 1; +// bundle2.putBoolean("result", false); +// return bundle2; +// } +// c = 65535; +// bundle2.putBoolean("result", false); +// return bundle2; +// } else if (hashCode == 214442514) { +// if (str.equals("mt:createSymlink")) { +// c = 2; +// bundle2.putBoolean("result", false); +// return bundle2; +// } +// c = 65535; +// bundle2.putBoolean("result", false); +// return bundle2; +// } else { +// if (hashCode == 1713485102 && str.equals("mt:setLastModified")) { +// c = 0; +// bundle2.putBoolean("result", false); +// message = "Unsupported method: ".concat(str); +// bundle2.putString("message", message); +// return bundle2; +// } +// c = 65535; +// bundle2.putBoolean("result", false); +// return bundle2; +// } +// } +// return null; +// } +// +// @Override // android.provider.DocumentsProvider +// public final String createDocument(String str, String str2, String str3) throws FileNotFoundException { +// StringBuilder sb; +// File b = b(str, true); +// if (b != null) { +// File file = new File(b, str3); +// int i = 2; +// while (file.exists()) { +// file = new File(b, str3 + " (" + i + ")"); +// i++; +// } +// try { +// if ("vnd.android.document/directory".equals(str2) ? file.mkdir() : file.createNewFile()) { +// if (str.endsWith("/")) { +// sb = new StringBuilder(); +// sb.append(str); +// sb.append(file.getName()); +// } else { +// sb = new StringBuilder(); +// sb.append(str); +// sb.append("/"); +// sb.append(file.getName()); +// } +// str = sb.toString(); +// return str; +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// throw new FileNotFoundException("Failed to create document in " + str + " with name " + str3); +// } +// +// public final void d(MatrixCursor matrixCursor, String str, File file) throws FileNotFoundException { +// int i; +// String name; +// if (file == null) { +// file = b(str, true); +// } +// boolean z = false; +// if (file == null) { +// MatrixCursor.RowBuilder newRow = matrixCursor.newRow(); +// newRow.add("document_id", this.b); +// newRow.add("_display_name", this.b); +// newRow.add("_size", 0L); +// newRow.add("mime_type", "vnd.android.document/directory"); +// newRow.add("last_modified", 0); +// newRow.add("flags", 0); +// return; +// } +// if (file.isDirectory()) { +// if (file.canWrite()) { +// i = 8; +// } +// i = 0; +// } else { +// if (file.canWrite()) { +// i = 2; +// } +// i = 0; +// } +// if (file.getParentFile().canWrite()) { +// i = i | 4 | 64; +// } +// String path = file.getPath(); +// if (path.equals(this.c.getPath())) { +// name = "data"; +// } else if (path.equals(this.d.getPath())) { +// name = "android_data"; +// } else if (path.equals(this.e.getPath())) { +// name = "android_obb"; +// } else { +// name = file.getName(); +// z = true; +// } +// MatrixCursor.RowBuilder newRow2 = matrixCursor.newRow(); +// newRow2.add("document_id", str); +// newRow2.add("_display_name", name); +// newRow2.add("_size", Long.valueOf(file.length())); +// newRow2.add("mime_type", c(file)); +// newRow2.add("last_modified", Long.valueOf(file.lastModified())); +// newRow2.add("flags", Integer.valueOf(i)); +// newRow2.add("mt_path", file.getAbsolutePath()); +// if (z) { +// try { +// StringBuilder sb = new StringBuilder(); +// StructStat lstat = Os.lstat(path); +// sb.append(lstat.st_mode); +// sb.append("|"); +// sb.append(lstat.st_uid); +// sb.append("|"); +// sb.append(lstat.st_gid); +// if ((lstat.st_mode & 61440) == 40960) { +// sb.append("|"); +// sb.append(Os.readlink(path)); +// } +// newRow2.add("mt_extras", sb.toString()); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// } +// +// @Override // android.provider.DocumentsProvider +// public final void deleteDocument(String str) throws FileNotFoundException { +// File b = b(str, true); +// if (b == null || !a(b)) { +// throw new FileNotFoundException("Failed to delete document ".concat(str)); +// } +// } +// +// @Override // android.provider.DocumentsProvider +// public final String getDocumentType(String str) throws FileNotFoundException { +// File b = b(str, true); +// return b == null ? "vnd.android.document/directory" : c(b); +// } +// +// @Override // android.provider.DocumentsProvider +// public final boolean isChildDocument(String str, String str2) { +// return str2.startsWith(str); +// } +// +// @Override // android.provider.DocumentsProvider +// public final String moveDocument(String str, String str2, String str3) throws FileNotFoundException { +// File b = b(str, true); +// File b2 = b(str3, true); +// if (b != null && b2 != null) { +// File file = new File(b2, b.getName()); +// if (!file.exists() && b.renameTo(file)) { +// if (str3.endsWith("/")) { +// return str3 + file.getName(); +// } +// return str3 + "/" + file.getName(); +// } +// } +// throw new FileNotFoundException("Filed to move document " + str + " to " + str3); +// } +// +// @Override // android.content.ContentProvider +// public final boolean onCreate() { +// return true; +// } +// +// @Override // android.provider.DocumentsProvider +// public final ParcelFileDescriptor openDocument(String str, String str2, CancellationSignal cancellationSignal) throws FileNotFoundException { +// File b = b(str, false); +// if (b != null) { +// return ParcelFileDescriptor.open(b, ParcelFileDescriptor.parseMode(str2)); +// } +// throw new FileNotFoundException(str.concat(" not found")); +// } +// +// @Override // android.provider.DocumentsProvider +// public final Cursor queryChildDocuments(String str, String[] strArr, String str2) throws FileNotFoundException { +// if (str.endsWith("/")) { +// str = str.substring(0, str.length() - 1); +// } +// if (strArr == null) { +// strArr = g; +// } +// MatrixCursor matrixCursor = new MatrixCursor(strArr); +// File b = b(str, true); +// if (b == null) { +// d(matrixCursor, str.concat("/data"), this.c); +// if (this.d.exists()) { +// d(matrixCursor, str.concat("/android_data"), this.d); +// } +// if (this.e.exists()) { +// d(matrixCursor, str.concat("/android_obb"), this.e); +// } +// } else { +// File[] listFiles = b.listFiles(); +// if (listFiles != null) { +// for (File file : listFiles) { +// d(matrixCursor, str + "/" + file.getName(), file); +// } +// } +// } +// return matrixCursor; +// } +// +// @Override // android.provider.DocumentsProvider +// public final Cursor queryDocument(String str, String[] strArr) throws FileNotFoundException { +// if (strArr == null) { +// strArr = g; +// } +// MatrixCursor matrixCursor = new MatrixCursor(strArr); +// d(matrixCursor, str, null); +// return matrixCursor; +// } +// +// @Override // android.provider.DocumentsProvider +// public final Cursor queryRoots(String[] strArr) { +// ApplicationInfo applicationInfo = getContext().getApplicationInfo(); +// String charSequence = applicationInfo.loadLabel(getContext().getPackageManager()).toString(); +// if (strArr == null) { +// strArr = f; +// } +// MatrixCursor matrixCursor = new MatrixCursor(strArr); +// MatrixCursor.RowBuilder newRow = matrixCursor.newRow(); +// newRow.add("root_id", this.b); +// newRow.add("document_id", this.b); +// newRow.add("summary", this.b); +// newRow.add("flags", 17); +// newRow.add("title", charSequence); +// newRow.add("mime_types", "*/*"); +// newRow.add("icon", Integer.valueOf(applicationInfo.icon)); +// return matrixCursor; +// } +// +// @Override // android.provider.DocumentsProvider +// public final void removeDocument(String str, String str2) throws FileNotFoundException { +// deleteDocument(str); +// } +// +// @Override // android.provider.DocumentsProvider +// public final String renameDocument(String str, String str2) throws FileNotFoundException { +// File b = b(str, true); +// if (b == null || !b.renameTo(new File(b.getParentFile(), str2))) { +// throw new FileNotFoundException("Failed to rename document " + str + " to " + str2); +// } +// int lastIndexOf = str.lastIndexOf(47, str.length() - 2); +// return str.substring(0, lastIndexOf) + "/" + str2; +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/eltechs/axs/Mcat.java b/app/src/main/java/com/eltechs/axs/Mcat.java new file mode 100644 index 00000000..1d3d46c1 --- /dev/null +++ b/app/src/main/java/com/eltechs/axs/Mcat.java @@ -0,0 +1,7 @@ +package com.eltechs.axs; + +public class Mcat { + public void start(){ + + }; +} diff --git a/app/src/main/java/com/eltechs/ed/fragments/ContainerSettingsFragment.java b/app/src/main/java/com/eltechs/ed/fragments/ContainerSettingsFragment.java index b1e585d6..7c74c50a 100644 --- a/app/src/main/java/com/eltechs/ed/fragments/ContainerSettingsFragment.java +++ b/app/src/main/java/com/eltechs/ed/fragments/ContainerSettingsFragment.java @@ -1,5 +1,7 @@ package com.eltechs.ed.fragments; +import static com.example.datainsert.exagear.RR.getS; + import android.app.AlertDialog; import android.content.DialogInterface; import android.content.SharedPreferences; @@ -35,22 +37,21 @@ public class ContainerSettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { public static final String ARG_CONT_ID = "CONT_ID"; - private static final int VERSION_FOR_EDPATCH = 2; private static final String TAG = "ContSettingsFragment"; /** * 如果能找到对应类,就启动对应功能。edpatch添加功能时复制对应类即可。 */ - private final static boolean enable_custom_resolution = false; - private final static boolean enable_different_renderers = false; + private final static boolean enable_custom_resolution = QH.classExist("com.example.datainsert.exagear.containerSettings.ConSetResolution"); + private final static boolean enable_different_renderers = QH.classExist("com.example.datainsert.exagear.containerSettings.ConSetRenderer"); public static String KEY_RENDERER = "RENDERER"; //偏好xml中渲染方式的KEY - /** * 用于记录本次dialog期间选定的分辨率 */ private String curResolution; + @Override // android.support.v7.preference.PreferenceFragmentCompat public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { long contId = (getArguments() != null) ? getArguments().getLong(ARG_CONT_ID) : 0L; @@ -72,7 +73,8 @@ private void buildRendererPref() { //新建preference时的context需要为 从已构建的preference获取的contextwrapper,否则样式会不同 ListPreference renderPref = new ListPreference(getPreferenceManager().getContext()); - renderPref.setTitle("Renderer"); + renderPref.setTitle(getS(RR.render_title)); + renderPref.setDialogTitle(getS(RR.render_title)); renderPref.setKey(KEY_RENDERER); renderPref.setSummary("%s"); renderPref.setOrder(3); @@ -156,7 +158,7 @@ private void buildResolutionDialog(Preference preference) { //添加自定义的选项 //开关 Switch switchToCustom = new Switch(requireContext()); - switchToCustom.setText(RR.getS(RR.CstRsl_swtTxt)); + switchToCustom.setText(getS(RR.CstRsl_swtTxt)); LinearLayout resolutionLinearLayout = new LinearLayout(requireContext()); resolutionLinearLayout.setOrientation(LinearLayout.VERTICAL); // LinearLayout rsSwitchLLayout = new LinearLayout(requireContext()); @@ -168,11 +170,11 @@ private void buildResolutionDialog(Preference preference) { //输入文本 LinearLayout rsEditLLayout = new LinearLayout(requireContext()); EditText widthEText = new EditText(requireContext()); - widthEText.setHint(RR.getS(RR.CstRsl_editW)); + widthEText.setHint(getS(RR.CstRsl_editW)); // widthEText.setInputType(InputType.TYPE_NULL); // widthEText.setFocusable(false); EditText heightEText = new EditText(requireContext()); - heightEText.setHint(RR.getS(RR.CstRsl_editH)); + heightEText.setHint(getS(RR.CstRsl_editH)); TextView commaText = new TextView(requireContext()); commaText.setText(","); rsEditLLayout.addView(widthEText); @@ -289,11 +291,14 @@ private void updatePreference(Preference preference) { * 需要在添加环境变量的那个action(即外部)获取对应的字符串,如果硬编码,modder需要修改两处,可能会忽略。用enum只需要改一处即可。 */ public enum renderEntries { - LLVMPipe("LLVMPipe", "usr/lib/llvm"), - VirGL_Overlay("VirGL Overlay", "usr/lib/virgloverlay"), - VirGL_built_in("VirGL built-in", "usr/lib/virglbuiltin"), - VirtIO_GPU("VirtIO-GPU", "usr/lib/virtiogpu"), - Zink_Turnip("Zink Turnip", "usr/lib/zinkturnip"); + LLVMPipe("LLVMPipe", "/opt/lib/llvm"), + VirGL_Overlay("VirGL Overlay", "/opt/lib/vo"), + VirGL_built_in("VirGL built-in", "/opt/lib/vb"), + VirtIO_GPU("VirtIO-GPU", "/opt/lib/vg"), + Turnip_Zink("Turnip Zink", "/opt/lib/tz"), + Turnip_DXVK("Turnip DXVK","/opt/lib/td"), + ; + public final String name; public final String path; diff --git a/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java b/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java index 891ebcca..10dd6f09 100644 --- a/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java +++ b/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java @@ -166,29 +166,29 @@ public void callWhenFirstStart(AppCompatActivity activity) { // e.printStackTrace(); // } - //启动pulseaudio (貌似多次启动会导致失效,要么就启动一次,要么就先停止再启动) - if (!pulseStarted) { - Log.d(TAG, "callWhenFirstStart: pulseaudio未启动,启动pulseaudio"); - //解压必要文件 - File paDir = new File(Globals.getAppContext().getFilesDir(), "pulseaudio-xsdl"); - if (paDir.exists() && (!paDir.isDirectory() || paDir.list().length == 0)) - paDir.delete();//解压要求paDir不存在 - ZipInstallerAssets.installIfNecessary(Globals.getAppContext(), new ZipInstallerAssets.InstallCallback() { - @Override - public void installationFailed(String str) { - Log.e(TAG, "installationFailed: ", new Exception(str)); - paDir.delete(); - } - - @Override - public void installationFinished(String str) { - Log.d(TAG, "installationFinished: " + str); - //设置pulseaudio路径并启动pulseaudio - setEnv(paDir.getAbsolutePath()); - pulseStarted = startPulseaudio() == 0; - } - }, paDir, "pulseaudio-xsdl.zip"); - } +// //启动pulseaudio (貌似多次启动会导致失效,要么就启动一次,要么就先停止再启动) +// if (!pulseStarted) { +// Log.d(TAG, "callWhenFirstStart: pulseaudio未启动,启动pulseaudio"); +// //解压必要文件 +// File paDir = new File(Globals.getAppContext().getFilesDir(), "pulseaudio-xsdl"); +// if (paDir.exists() && (!paDir.isDirectory() || paDir.list().length == 0)) +// paDir.delete();//解压要求paDir不存在 +// ZipInstallerAssets.installIfNecessary(Globals.getAppContext(), new ZipInstallerAssets.InstallCallback() { +// @Override +// public void installationFailed(String str) { +// Log.e(TAG, "installationFailed: ", new Exception(str)); +// paDir.delete(); +// } +// +// @Override +// public void installationFinished(String str) { +// Log.d(TAG, "installationFinished: " + str); +// //设置pulseaudio路径并启动pulseaudio +// setEnv(paDir.getAbsolutePath()); +// pulseStarted = startPulseaudio() == 0; +// } +// }, paDir, "pulseaudio-xsdl.zip"); +// } } @Override diff --git a/app/src/main/java/com/example/datainsert/exagear/QH.java b/app/src/main/java/com/example/datainsert/exagear/QH.java index 4c225a8f..f1138103 100644 --- a/app/src/main/java/com/example/datainsert/exagear/QH.java +++ b/app/src/main/java/com/example/datainsert/exagear/QH.java @@ -9,12 +9,18 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.RippleDrawable; +import android.support.v4.app.FragmentActivity; import android.support.v4.graphics.ColorUtils; import android.support.v4.view.ViewCompat; +import android.support.v7.app.AlertDialog; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; import com.eltechs.axs.Globals; import com.eltechs.axs.activities.FrameworkActivity; @@ -135,4 +141,56 @@ public static void setRippleBackground(View view){ background = new RippleDrawable(ColorStateList.valueOf(Color.GRAY), contentDrawable, maskDrawable); ViewCompat.setBackground(view, background); } + + /** + * 测试某个包名是否存在。用于多个功能使用同一个类时,该类判断哪些功能是添加的,哪些是没添加的 + * @param name 类完整名(包名,类名) + * @return 是否存在. 在自己测试apk中始终返回 true + */ + public static boolean classExist( String name){ + if(QH.isTesting()) + return true; + boolean exist = false; + try { + Class.forName(name); + exist = true; + } catch (Exception ignored) { + } + return exist; + } + + /** + * 构建一个对话框。显示一条消息以及 下次不再提示的按钮。 + * 若已经设置过不再提示则不会显示 + * @param a context 不能为global获取的 + * @param tips 文字内容 + * @param PREF_KEY_SHOULD_SHOW_TIP 写到QH.getPreference()里的key。若为true则显示对话框 + */ + public static void showTipDialogWithDisable(Context a,String tips,String PREF_KEY_SHOULD_SHOW_TIP){ + //如果已经设置过,就不再显示 + if(!QH.isTesting() && !QH.getPreference().getBoolean(PREF_KEY_SHOULD_SHOW_TIP,true)) + return; + + TextView textView = new TextView(a); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + textView.setLineSpacing(0, 1.5f); + textView.setText(tips); + + CheckBox checkBox = new CheckBox(a); + checkBox.setText(RR.getS(RR.shortcut_DontShowUp)); + checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> QH.getPreference().edit().putBoolean(PREF_KEY_SHOULD_SHOW_TIP, !isChecked).apply()); + LinearLayout.LayoutParams checkParams = new LinearLayout.LayoutParams(-2, -2); + checkParams.topMargin = 20; + + LinearLayout linearLayout = new LinearLayout(a); + linearLayout.setOrientation(LinearLayout.VERTICAL); + int padding = QH.px(a, RR.attr.dialogPaddingDp); + linearLayout.setPadding(padding, padding, padding, padding); + linearLayout.addView(textView); + linearLayout.addView(checkBox, checkParams); + ScrollView scrollView = new ScrollView(a); + scrollView.addView(linearLayout); + new AlertDialog.Builder(a).setView(scrollView).setPositiveButton(android.R.string.yes, null).create().show(); + + } } diff --git a/app/src/main/java/com/example/datainsert/exagear/RR.java b/app/src/main/java/com/example/datainsert/exagear/RR.java index 9d75e0a7..ec1a2c7c 100644 --- a/app/src/main/java/com/example/datainsert/exagear/RR.java +++ b/app/src/main/java/com/example/datainsert/exagear/RR.java @@ -8,6 +8,9 @@ import java.util.Map; import java.util.Objects; +//函数名,函数参数,变量名也不能随意改。这样用户添加的旧功能,尝试读取旧变量名,添加另外一个功能时RR被复制成新的,旧变量名就没了 + +//使用sparseArray,可以不连续的数值,可以不按顺序的数值。在版本更迭的时候,数值可以更改,不影响未更新的功能,但是成员变量名不能更改。 public class RR { /** * 存储按键按钮的framelayout布局.在dialogfragment里用这个来寻找当前是否存在该布局 @@ -128,13 +131,14 @@ public class RR { public static int mw_refreshBtn = 109; public static int mw_dlSourceBtn = 110; public static int mw_dataSizeMB = 111; - public static int mw_dialog_download = 112; public static int mw_dialog_extract = 113; public static int mw_dialog_checksum = 114; public static int mw_localMenuItem = 115; public static int mw_localState = 116; public static int mw_tips = 117; + public static int mw_contNoWineTips = 118; + public static int render_title = 119; public static String locale = refreshLocale(); @@ -273,6 +277,8 @@ public static String refreshLocale(){ "
  •   “本地”页面:
    对已下载或预置的wine进行管理。点击安装(解压)后,会显示“已启用”,已启用的wine会显示在新建容器时的选项中。可通过卸载(删除已解压文件夹)来减少本地占用。本地存放位置为:z:/opt/WineCollection。
  • \n" + "
  •   “可下载”页面:
    从网络下载更多版本的wine。下载源可选择WineHQ(官方构建)或Kron4ek(第三方,体积小),其中WineHQ仅提供 ubuntu 18 的对应列表,Kron4ek不提供 staging 版本。下载成功后会显示在“本地”页面。若由于网络原因下载失败,可以尝试切换下载线路。
  • \n" + "\n"); + zhArray.put(mw_contNoWineTips, "没有检测到已启用的wine,本次创建的容器可能无法启动。建议删除该容器,点击下载按钮下载并安装wine后,重新创建容器。"); + zhArray.put(render_title,"图形渲染设置"); /* @@ -410,6 +416,8 @@ public static String refreshLocale(){ "
  •   Local:
    Edit the downloaded or bundled Wines. After Clicking Install (extract) option, it will be displayed as 'active', which can be selected when creating a new container. Use Uninstall (delete extracted folder) option to reduce local storage. Wines are stored at z:/opt/WineCollection.
  • \n" + "
  •   Downloadable:
    Download all kinds of Wines from the Internet. Available sources are WineHQ(Official build, only ubuntu18-builds are listed) and Kron4ek(shrinked size, staging versions are not included). Downloaded Wines appear at 'Local' page.
  • \n" + "\n"); + enArray.put(mw_contNoWineTips, "No active Wine detected. This container probably can not launch. Please delete it, click the download button to install Wines and try again."); + enArray.put(render_title,"Renderer"); /* @@ -549,6 +557,8 @@ public static String refreshLocale(){ "
  •   Установка wine:
    Вы можете редактировать загруженные или предустановленные версии wine. После выбора опции Установить (Извлечь) выбранная версия wine будет отображаться как 'Активная', теперь её можно выбрать при создании нового контейнера. Используйте опцию Удалить (удалить папку с wine), чтобы уменьшить объем затятой внутренней памяти вашего девайса. Файлы wine хранятся в папке Z:/opt/WineCollection.
  • \n" + "
  •   Загрузка файлов wine:
    загрузка всех видов wine из интернета. Доступные источники: WineHQ (официальная сборка, перечислены только сборки ubuntu18) и Kron4ek (уменьшенный размер, промежуточные версии wine не включены). Загруженные версии wine появляются на странице 'Установленные'.
  • \n" + "\n"); + ruArray.put(mw_contNoWineTips, "Активный Wine не найден. Этот контейнер вероятно, не может запуститься. Удалите его, затем нажмите кнопку загрузки, чтобы установить необходимый Wine и повторите попытку."); + ruArray.put(render_title,"Renderer"); stringMap.put("zh", zhArray); stringMap.put("en", enArray); diff --git a/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java b/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java index 2864773a..d704e29f 100644 --- a/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java +++ b/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java @@ -1,17 +1,21 @@ package com.example.datainsert.exagear.action; +import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.Turnip_DXVK; import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.VirGL_Overlay; import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.VirGL_built_in; +import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.VirtIO_GPU; import static com.eltechs.ed.guestContainers.GuestContainerConfig.CONTAINER_CONFIG_FILE_KEY_PREFIX; import static com.example.datainsert.exagear.mutiWine.MutiWine.KEY_WINE_INSTALL_PATH; import android.content.Context; import android.content.SharedPreferences; import android.os.Build; +import android.os.Environment; import android.util.Log; import com.eltechs.axs.Globals; +import com.eltechs.axs.Mcat; import com.eltechs.axs.applicationState.EnvironmentAware; import com.eltechs.axs.applicationState.ExagearImageAware; import com.eltechs.axs.applicationState.UBTLaunchConfigurationAware; @@ -19,6 +23,7 @@ import com.eltechs.axs.configuration.startup.actions.AbstractStartupAction; import com.eltechs.ed.fragments.ContainerSettingsFragment; import com.eltechs.ed.guestContainers.GuestContainersManager; +import com.example.datainsert.exagear.QH; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; @@ -104,12 +109,15 @@ private void addRendererPath(SharedPreferences sp, UBTLaunchConfiguration ubtCon //调用so File virglServerFile = new File(getAppContext().getApplicationInfo().nativeLibraryDir, "libvirgl_test_server.so"); try { - ProcessBuilder builder = new ProcessBuilder(virglServerFile.getAbsolutePath(), ""); + ProcessBuilder builder = new ProcessBuilder(virglServerFile.getAbsolutePath()); builder.environment().put("TMPDIR", getApplicationState().getExagearImage().getPath().getAbsolutePath() + "/tmp"); +// builder.environment().put("VTEST_SOCK", ""); // builder.directory(paDir); builder.redirectErrorStream(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.redirectOutput(new File("/sdcard/virglLog.txt")); + File logDir = new File(Globals.getAppContext().getExternalFilesDir(null),"logs"); + logDir.mkdirs(); + builder.redirectOutput(new File(logDir,"virglLog.txt")); } builder.start(); @@ -117,6 +125,12 @@ private void addRendererPath(SharedPreferences sp, UBTLaunchConfiguration ubtCon } catch (IOException e) { e.printStackTrace(); } + } else if (testRenderExist("VirtIO_GPU") && VirtIO_GPU.name.equals(rendererName)) { + if (QH.classExist("com.eltechs.axs.MCat")) + new Mcat().start(); + } else if (testRenderExist("Turnip_DXVK") && Turnip_DXVK.name.equals(rendererName)) { + ubtConfig.addEnvironmentVariable("GALLIUM_DRIVER", "zink"); + ubtConfig.addEnvironmentVariable("MESA_VK_WSI_DEBUG", "sw"); } if (!ldPath.equals("")) diff --git a/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java b/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java new file mode 100644 index 00000000..78af3b5d --- /dev/null +++ b/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java @@ -0,0 +1,5 @@ +package com.example.datainsert.exagear.containerSettings; + +public class ConSetRenderer { + private static final int VERSION_FOR_EDPATCH = 1; +} diff --git a/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetResolution.java b/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetResolution.java new file mode 100644 index 00000000..c93a18b7 --- /dev/null +++ b/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetResolution.java @@ -0,0 +1,9 @@ +package com.example.datainsert.exagear.containerSettings; + +import android.content.Context; +import android.support.v7.preference.ListPreference; + +public class ConSetResolution { + private static final int VERSION_FOR_EDPATCH = 2; + +} diff --git a/app/src/main/java/com/example/datainsert/exagear/mutiWine/MutiWine.java b/app/src/main/java/com/example/datainsert/exagear/mutiWine/MutiWine.java index ce7204fc..aa40cf10 100644 --- a/app/src/main/java/com/example/datainsert/exagear/mutiWine/MutiWine.java +++ b/app/src/main/java/com/example/datainsert/exagear/mutiWine/MutiWine.java @@ -2,18 +2,27 @@ import static com.example.datainsert.exagear.RR.getS; +import static com.example.datainsert.exagear.mutiWine.WineVersion.CONTAINER_NAME_NO_WINE; import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.content.Context; import android.content.SharedPreferences; import android.os.AsyncTask; +import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.util.Log; +import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; import com.eltechs.axs.Globals; import com.eltechs.axs.applicationState.ApplicationStateBase; @@ -39,10 +48,13 @@ public class MutiWine { private static final int VERSION_FOR_EDPATCH = 2; private static final String TAG = "MutiWine"; + /** + * 创建容器时没有识别到已启用的wine,是否显示 + */ + private static final String PREF_KEY_SHOULD_SHOW_TIP_CONTAINER_NO_WINE = "SHOULD_SHOW_TIP_CONTAINER_NO_WINE"; //pref前缀,用于容器设置,记录wine版本,需要根据apk包名自行修改 //内容有:wineVersion,wineExecutePath public static String CONTAINER_CONFIG_FILE_KEY_PREFIX = GuestContainerConfig.CONTAINER_CONFIG_FILE_KEY_PREFIX; - //pref前缀,用于新建容器时临时记录wine版本,和启动容器时临时记录wine版本 //内容有:新建容器时 wineVersion,wineExecutePath,winePatternPath public static String TMP_WINE_VER_PREF = "tmpWineVerPref"; @@ -124,7 +136,6 @@ making modifications to it as desired, until the next time onCreateOptionsMenu() //为什么子菜单的内容不会进到这个监听啊(啊原来只监听本menuitem。。。) } - /** * 启动容器时,在StartGuest里调用,添加环境变量(wine的执行路径和链接库路径) * @@ -185,7 +196,6 @@ public static void addEnvVars(Long id, List list) { // Log.d(TAG, "getEnvVarBin: 链接库路径为" + list.get(list.size() - 1) + ", 所选模式为" + renderer); } - /** * 新建容器时,从临时pref中读取wine版本信息,并写入新建容器的设置pref中。 * 顺便设置一下容器名。放在loadDefault()的结尾,这样开头写入Container_n就会被此时的名字覆盖 @@ -265,8 +275,8 @@ protected void onPreExecute() { mIsAsyncTaskRun = true; mProgressDialog = ProgressDialog.show(mFragment.getContext(), "", getS(RR.mw_newContProgress), true, false); //如果guestcont-pattern不存在手动创建 - File patternFile = new File(((ExagearImageAware) Globals.getApplicationState()).getExagearImage().getPath(), "opt/guestcont-pattern/"); - if(!patternFile.exists()) + File patternFile = new File(((ExagearImageAware) Globals.getApplicationState()).getExagearImage().getPath(), "opt/guestcont-pattern/"); + if (!patternFile.exists()) patternFile.mkdirs(); } @@ -307,6 +317,14 @@ public void onPostExecute(Void r2) { // mFragment.refreshContainersList(); mProgressDialog.dismiss(); mIsAsyncTaskRun = false; + + //如果没识别到wine,提醒一下用户吧 + if (mWineVersion.name.equals(CONTAINER_NAME_NO_WINE)) { + QH.showTipDialogWithDisable( + mFragment.requireActivity(), + getS(RR.mw_contNoWineTips), + PREF_KEY_SHOULD_SHOW_TIP_CONTAINER_NO_WINE); + } } } diff --git a/app/src/main/java/com/example/datainsert/exagear/mutiWine/WineVersion.java b/app/src/main/java/com/example/datainsert/exagear/mutiWine/WineVersion.java index cfb49b95..bedbbc76 100644 --- a/app/src/main/java/com/example/datainsert/exagear/mutiWine/WineVersion.java +++ b/app/src/main/java/com/example/datainsert/exagear/mutiWine/WineVersion.java @@ -14,6 +14,7 @@ public class WineVersion { //wine版本信息列表 public static ArrayList wineList = new ArrayList<>(); + static final String CONTAINER_NAME_NO_WINE="Container"; /** * 用于按钮显示,不一定表示对应的tag名 */ @@ -71,7 +72,7 @@ public static void initList() { //如果一个都没找到,至少添加一个 if (wineList.isEmpty()) - wineList.add(new WineVersion("No active Wine found", "")); + wineList.add(new WineVersion(CONTAINER_NAME_NO_WINE, "")); //按名称 排下序 Collections.sort(wineList, new WineNameComparator()); diff --git a/app/src/main/java/com/example/datainsert/exagear/shortcut/MoreShortcut.java b/app/src/main/java/com/example/datainsert/exagear/shortcut/MoreShortcut.java index 7acb418d..78794474 100644 --- a/app/src/main/java/com/example/datainsert/exagear/shortcut/MoreShortcut.java +++ b/app/src/main/java/com/example/datainsert/exagear/shortcut/MoreShortcut.java @@ -1,12 +1,18 @@ package com.example.datainsert.exagear.shortcut; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.media.MediaScannerConnection; +import android.net.Uri; +import android.os.FileUriExposedException; import android.os.PersistableBundle; +import android.support.v4.content.ContentResolverCompat; +import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; @@ -19,6 +25,7 @@ import com.eltechs.axs.Globals; import com.eltechs.axs.applicationState.ApplicationStateBase; +import com.eltechs.ed.BuildConfig; import com.eltechs.ed.EDApplicationState; import com.eltechs.ed.XDGLink; import com.eltechs.ed.activities.EDStartupActivity; @@ -101,6 +108,16 @@ public static void addOptionsToMenu(boolean isStartMenu, PopupMenu popupMenu, XD }); +// popupMenu.getMenu().add("编辑").setOnMenuItemClickListener(item->{ +// Intent shareIntent = new Intent(); +// shareIntent.setAction(Intent.ACTION_SEND); +// shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); +// Uri uri = FileProvider.getUriForFile(Globals.getAppContext(), BuildConfig.APPLICATION_ID.concat(".provider"), xdgLink.linkFile); +// shareIntent.putExtra(Intent.EXTRA_STREAM, uri); +// shareIntent.setType("text/plain"); +// Globals.getAppContext().startActivity(shareIntent); +// return true; +// }); } diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..3f61545e --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/patchapp/build.gradle b/patchapp/build.gradle index 5bf67bc1..6091cc06 100644 --- a/patchapp/build.gradle +++ b/patchapp/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.ewt45.patchapp" minSdk 21 targetSdk 27 - versionCode 4 - versionName "0.0.3" + versionCode 5 + versionName '0.0.5' resourceConfigurations += ['en', 'zh', 'ru'] testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/patchapp/release/output-metadata.json b/patchapp/release/output-metadata.json index 43c1a21a..f095b134 100644 --- a/patchapp/release/output-metadata.json +++ b/patchapp/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 4, - "versionName": "0.0.3", + "versionCode": 5, + "versionName": "0.0.5", "outputFile": "patchapp-release.apk" } ], diff --git a/patchapp/src/main/java/com/ewt45/patchapp/ActivityPatch.java b/patchapp/src/main/java/com/ewt45/patchapp/ActivityPatch.java index bee5b751..bec451b6 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/ActivityPatch.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/ActivityPatch.java @@ -12,6 +12,7 @@ import com.ewt45.patchapp.databinding.ActivityPtMainBinding; import com.ewt45.patchapp.unused.MyAdapter; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -53,6 +54,7 @@ protected void onCreate(Bundle savedInstanceState) { File patcher = PatchUtils.getLocalPatcherApk(); if (patcher.exists()) { boolean b = patcher.delete(); + Log.d("TAG", "onCreate: 发现更新的patcher.删除本地patcher.apk"); } } diff --git a/patchapp/src/main/java/com/ewt45/patchapp/PatchUtils.java b/patchapp/src/main/java/com/ewt45/patchapp/PatchUtils.java index 35bf891e..097c853d 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/PatchUtils.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/PatchUtils.java @@ -151,7 +151,7 @@ public static boolean isPatcherApkChanged(Context context) { try (InputStream is = context.getAssets().open("patcher/release/patcher.apk"); ByteArrayOutputStream bos = new ByteArrayOutputStream();) { IOUtils.copy(is, bos); - assetSha = bos.toByteArray(); + assetSha = digest.digest(bos.toByteArray()); } return !Arrays.equals(localSha, assetSha); diff --git a/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java b/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java index 41e626f2..dee74055 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java @@ -27,6 +27,7 @@ import com.ewt45.patchapp.thread.FuncCursor; import com.ewt45.patchapp.thread.FuncFAB; import com.ewt45.patchapp.thread.FuncMultiWine; +import com.ewt45.patchapp.thread.FuncRenderer; import com.ewt45.patchapp.thread.FuncResl; import com.ewt45.patchapp.thread.FuncSInput; import com.ewt45.patchapp.thread.FuncSelObb; @@ -82,6 +83,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, funcList.add(new FuncWithCheckBox(binding.checkSelobb, new FuncSelObb())); funcList.add(new FuncWithCheckBox(binding.checkShortcut,new FuncShortcut())); funcList.add(new FuncWithCheckBox(binding.checkMw,new FuncMultiWine())); + funcList.add(new FuncWithCheckBox(binding.checkRenderer,new FuncRenderer())); return binding.getRoot(); } diff --git a/patchapp/src/main/java/com/ewt45/patchapp/patching/PatcherFile.java b/patchapp/src/main/java/com/ewt45/patchapp/patching/PatcherFile.java index 148545dc..802f3102 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/patching/PatcherFile.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/patching/PatcherFile.java @@ -6,27 +6,16 @@ import com.ewt45.patchapp.thread.Func; import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.checkerframework.checker.units.qual.A; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; /** * 用于自己的apk的smali加入到exagear 的smali中 @@ -37,12 +26,23 @@ public class PatcherFile { static String TAG = "PatcherFile"; /** + * {@link #copy(int, String[], String[], String)} + */ + public static void copy(int type, String[] strings) throws Exception{ + copy(type,strings,new String[0],null); + } + + + /** + * * 将自己的apk中的smali复制到exagear的输出文件夹中 * * @param type 类型,目前仅支持smali和assets - * @param strings 要复制的文件名。格式如:"/com/a/b/*", "/com/a/b/c.smali" 斜线开头,星号或文件名结尾(不要星号了) + * @param names 要复制的文件名。格式如:"/com/a/b", "/com/a/b/c.smali" 斜线开头,完整文件(夹)名结尾 + * @param preservedFields 需要从旧smali复制过来的成员变量名(用于标识其他功能是否启用) + * @param setFieldLine 当前添加功能对应成员变量的完整行。e.g. {@code .field private static final enable_custom_resolution:Z = true} */ - public static void copy(int type, String[] strings) throws Exception { + public static void copy(int type, String[] names, String[] preservedFields, String setFieldLine) throws Exception { // if(type!=TYPE_SMALI) // throw new Exception("不支持的类型:"+type); String subfolder; @@ -57,7 +57,7 @@ else if (type == TYPE_ASSETS) LinkedList fileList = new LinkedList<>(); //要求传的不带*和/了,文件还是文件夹自己判断 - for (String str : strings) { + for (String str : names) { File oneFile = new File(PatchUtils.getPatchTmpDir().getAbsolutePath() + "/patcher/" + subfolder + str); if (oneFile.isDirectory()) fileList.addFirst(oneFile); @@ -96,13 +96,20 @@ else if (type == TYPE_ASSETS) .replace("com/eltechs/ed", PatchUtils.getPackageName()); //复制过去 File dstFile = new File(dstPath); + //考虑要保留的成员变量flag + Map oldFields = getPreservedFields(dstFile, preservedFields); FileUtils.copyFile(file, dstFile); //如果需要,替换代码中包名.不对,路径包含包名和文本包含包名没有关系,应该对每个文件文本都进行检查 - parsePkgNameInFile(dstFile); + List allLines = PatchUtils.scanAndParsePkgName(FileUtils.readLines(dstFile,StandardCharsets.UTF_8).toArray(new String[0])); + + restorePreservedFields(allLines,oldFields,setFieldLine); + + dstFile.delete(); + FileUtils.writeLines(dstFile,StandardCharsets.UTF_8.name(),allLines); } } else { //TODO: 星号判断去掉 - for (String str : strings) { + for (String str : names) { String srcPath = PatchUtils.getPatchTmpDir().getAbsolutePath() + "/patcher/" + subfolder + str; String dstPath = PatchUtils.getPatchTmpDir().getAbsolutePath() + "/tmp/" + subfolder + str; File srcFile = new File(srcPath); @@ -131,6 +138,61 @@ else if (type == TYPE_ASSETS) } + /** + * 复制文件后,将旧文件中的指定成员变量复原 + * + * @param allLines 该文件所有行 + * @param oldFields 指定成员变量及其对应的完整行 + * @param setFieldLine + */ + private static void restorePreservedFields(List allLines, Map oldFields, String setFieldLine) { + for(String field: oldFields.keySet()){ + for(int i=0; i getPreservedFields(File dstFile, String[] preservedFields) throws IOException { + Map fieldsMap = new HashMap<>(); + if(!dstFile.getName().endsWith(".smali") || !dstFile.exists()) + return fieldsMap; + + List allLines = FileUtils.readLines(dstFile,StandardCharsets.UTF_8); + for(String field:preservedFields){ + for(String line:allLines){ + if(line.trim().startsWith(".field") && line.contains("static final") && line.contains(field)) + fieldsMap.put(field,line); + } + } + + return fieldsMap; + } + /** * 将文件中代码替换包名为对应apk的包名 @@ -140,34 +202,39 @@ else if (type == TYPE_ASSETS) */ private static void parsePkgNameInFile(File dstFile) { try { - //获取文件内容并替换包名(类形式,字符串形式) - BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(dstFile), StandardCharsets.UTF_8)); - String line; - List mAllLines = new ArrayList<>(); - //找到该smali类名 - while ((line = br.readLine()) != null) { - //如果不是空行的话就添加到列表中 - if (!line.equals("")) { - mAllLines.add(line - .replace("com/eltechs/ed", PatchUtils.getPackageName()) - .replace("com.eltechs.ed", PatchUtils.getPackageName().replace('/', '.'))); - } - } - br.close(); - //生成新文件 - File newFile = new File(dstFile.getAbsolutePath() + ".1"); //修改后的文件 - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), StandardCharsets.UTF_8)); - for (String s : mAllLines) { - bw.write(s); - bw.newLine(); - } - bw.flush(); - bw.close(); - //重命名修改后的文件 + List allLines = PatchUtils.scanAndParsePkgName(FileUtils.readLines(dstFile,StandardCharsets.UTF_8).toArray(new String[0])); dstFile.delete(); - if (!newFile.renameTo(dstFile)) { - Log.e(TAG, "close: 新建的临时smali文件无法重命名为原本的smali文件", new Exception("重命名失败")); - } + FileUtils.writeLines(dstFile,StandardCharsets.UTF_8.name(),allLines); + + +// //获取文件内容并替换包名(类形式,字符串形式) +// BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(dstFile), StandardCharsets.UTF_8)); +// String line; +// List mAllLines = new ArrayList<>(); +// //找到该smali类名 +// while ((line = br.readLine()) != null) { +// //如果不是空行的话就添加到列表中 +// if (!line.equals("")) { +// mAllLines.add(line +// .replace("com/eltechs/ed", PatchUtils.getPackageName()) +// .replace("com.eltechs.ed", PatchUtils.getPackageName().replace('/', '.'))); +// } +// } +// br.close(); +// //生成新文件 +// File newFile = new File(dstFile.getAbsolutePath() + ".1"); //修改后的文件 +// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), StandardCharsets.UTF_8)); +// for (String s : mAllLines) { +// bw.write(s); +// bw.newLine(); +// } +// bw.flush(); +// bw.close(); +// //重命名修改后的文件 +// dstFile.delete(); +// if (!newFile.renameTo(dstFile)) { +// Log.e(TAG, "close: 新建的临时smali文件无法重命名为原本的smali文件", new Exception("重命名失败")); +// } } catch (Exception e) { e.printStackTrace(); diff --git a/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java b/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java index 0c20910e..9562aaa5 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java @@ -415,6 +415,10 @@ public static int findVersionInClass(String targetClassName){ return version; } + public void getStaticFinalField(){ + + } + // public static class ModPosition { // diff --git a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncFAB.java b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncFAB.java index 7f9194f6..13059efe 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncFAB.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncFAB.java @@ -35,7 +35,7 @@ public class FuncFAB implements Func { public Integer call() throws Exception { Sub1DriveD sub1DriveD = new Sub1DriveD(); Sub2Control sub2Control = new Sub2Control(); - Sub3Pulseaudio sub3Pulseaudio = new Sub3Pulseaudio(); +// Sub3Pulseaudio sub3Pulseaudio = new Sub3Pulseaudio(); int mergeVersion = getInstalledVersion(); @@ -55,10 +55,10 @@ public Integer call() throws Exception { sub2Control.firstInstall(); } - if (((mergeVersion >> 8) & 0x0000000f) == INVALID_VERSION) { - Log.d(TAG, "call: 首次安装子功能-pulseaudio"); - sub3Pulseaudio.firstInstall(); - } +// if (((mergeVersion >> 8) & 0x0000000f) == INVALID_VERSION) { +// Log.d(TAG, "call: 首次安装子功能-pulseaudio"); +// sub3Pulseaudio.firstInstall(); +// } //复制自己的类 Log.d(TAG, "btnStartPatch: 开始复制自己的smali"); @@ -70,7 +70,7 @@ public Integer call() throws Exception { //复制子功能自己的类 sub1DriveD.updateSelfPackage(); sub2Control.updateSelfPackage(); - sub3Pulseaudio.updateSelfPackage(); +// sub3Pulseaudio.updateSelfPackage(); return R.string.actmsg_funcfab; } diff --git a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java new file mode 100644 index 00000000..23477937 --- /dev/null +++ b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java @@ -0,0 +1,37 @@ +package com.ewt45.patchapp.thread; + +import static com.ewt45.patchapp.patching.PatcherFile.TYPE_SMALI; + +import com.ewt45.patchapp.R; +import com.ewt45.patchapp.patching.PatcherFile; +import com.ewt45.patchapp.patching.SmaliFile; + +public class FuncRenderer implements Func { + @Override + public int getStartMessage() { + return R.string.funcname_renderer; + } + + @Override + public int getInstalledVersion() { + return SmaliFile.findVersionInClass("com.example.datainsert.exagear.containerSettings.ConSetRenderer"); + } + + @Override + public int getLatestVersion() { + return 1; + } + + @Override + public Integer call() throws Exception { + + PatcherFile.copy(TYPE_SMALI, new String[]{ + "/com/eltechs/ed/fragments/ContainerSettingsFragment.smali", + "/com/example/datainsert/exagear/containerSettings/ConSetRenderer.smali", //复制标识类 + "/com/example/datainsert/exagear/QH.smali", + "/com/example/datainsert/exagear/RR.smali"}); + + + return R.string.funcname_renderer; + } +} diff --git a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncResl.java b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncResl.java index bb436e0b..7aed079b 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncResl.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncResl.java @@ -10,17 +10,20 @@ import java.io.File; /** - * 版本号改为2. 添加了中英双语 */ + * 版本号改为2. 添加了中英双语 + */ public class FuncResl implements Func { private static final String TAG = "FuncResl"; + @Override public int getLatestVersion() { return 2; } + @Override public int getInstalledVersion() { - int version = SmaliFile.findVersionInClass("com.eltechs.ed.fragments.ContainerSettingsFragment"); - if(version!=INVALID_VERSION) + int version = SmaliFile.findVersionInClass("com.eltechs.ed.fragments.ConSetResolution"); + if (version != INVALID_VERSION) return version; try { @@ -44,9 +47,14 @@ private boolean isPatchedOldWay() { @Override public Integer call() throws Exception { +// PatcherFile.copy(TYPE_SMALI, +// new String[]{"/com/eltechs/ed/fragments/ContainerSettingsFragment.smali"}, +// new String[]{"enable_different_renderers"}, +// ".field private static final enable_custom_resolution:Z = true"); PatcherFile.copy(TYPE_SMALI, new String[]{ "/com/eltechs/ed/fragments/ContainerSettingsFragment.smali", + "/com/example/datainsert/exagear/containerSettings/ConSetResolution.smali", //复制标识类 "/com/example/datainsert/exagear/QH.smali", "/com/example/datainsert/exagear/RR.smali"}); diff --git a/patchapp/src/main/res/layout/fragment_choose_patch.xml b/patchapp/src/main/res/layout/fragment_choose_patch.xml index b2b86e14..3eb7de17 100644 --- a/patchapp/src/main/res/layout/fragment_choose_patch.xml +++ b/patchapp/src/main/res/layout/fragment_choose_patch.xml @@ -106,6 +106,11 @@ android:text="@string/funcname_mw" android:layout_width="wrap_content" android:layout_height="wrap_content"/> + добавление функции: кнопки настроек добавление функции: выбора разрешений экрана Кнопка настроек\n- Выбор локации диска D\n- Кастомное управление - Выбор разрешений экрана + Настройки контейнера - Кастомное разрешение Исправление работы клавиатуры на android 11+ запуск исправлений установить исправленный apk @@ -71,4 +71,5 @@ добавление функции: MultiWine v2 Добавляется значок загрузки необходимой версии wine в левом верхнем углу страницы \"создание контейнеров\", тапните на вкладку \"добавление/удаление wine\".\n- Установка wine: Вы можете редактировать загруженные или предустановленные версии wine. После выбора опции Установить (Извлечь) выбранная версия wine будет отображаться как \'Активная\', теперь её можно выбрать при создании нового контейнера. Используйте опцию Удалить (удалить папку с wine), чтобы уменьшить объем занятой внутренней памяти вашего девайса. Файлы wine хранятся в папке Z:/opt/WineCollection.\n- Загрузка файлов wine: загрузка всех видов wine из интернета. Доступные источники: WineHQ (официальная сборка, перечислены только сборки ubuntu18) и Kron4ek (уменьшенный размер, промежуточные версии wine не включены). Загруженные версии wine появляются на странице \'Установленные\'.\nКак добавить предустановленные версии wine в кэши:\n`/opt/WineCollection/custom/$TagFolder/$WineFolder/bin/wine`\n- `$TagFolder` : имя папки необходимого wine, оно отображается при создании контейнеров, и должно быть уникальным.\n- `$WineFolder`: он должен содержать двоичный файл `./bin/wine`.\n- так же в `$TagFolder` должен быть `.tar.xz` архивный файл, из которого будет извлечена `$WineFolder`. MultiWine v2 + Настройки контейнера - Выбор рендера \ No newline at end of file diff --git a/patchapp/src/main/res/values-zh/strings.xml b/patchapp/src/main/res/values-zh/strings.xml index 67afc177..19a9ac99 100644 --- a/patchapp/src/main/res/values-zh/strings.xml +++ b/patchapp/src/main/res/values-zh/strings.xml @@ -23,7 +23,7 @@ 自定义D盘路径(悬浮操作按钮) 自定义操作模式(悬浮操作按钮) 强制显示鼠标光标 - 自定义分辨率 + 环境设置 - 自定义分辨率 修复安卓11及以上输入法闪退 初次启动手动选择obb 开始修改 @@ -72,5 +72,6 @@ 添加功能: 多版本wine共存 v2 在容器管理界面右上角新增下载按钮,点击进入wine版本管理界面。\n- 本地:对已下载或预置的wine进行管理。点击安装(解压)后,会显示“已启用”,已启用的wine会显示在新建容器时的选项中。可通过卸载(删除已解压文件夹)来减少本地占用。本地存放位置为:z:/opt/WineCollection。\n- 可下载:从网络下载更多版本的wine。下载源可选择WineHQ(官方构建)或Kron4ek(第三方,体积小),其中WineHQ仅提供 ubuntu 18 的对应列表,Kron4ek不提供 staging 版本。下载成功后会显示在“本地”页面。若由于网络原因下载失败,可以尝试切换下载线路。\n\n如何在数据包中预置wine:\n一个wine二进制文件的路径:`/opt/WineCollection/custom/$TagFolder/$WineFolder/bin/wine`\n- `$TagFolder` : 该文件夹名对应一个版本的wine名字,用于新建容器时显示。\n- `$WineFolder` : 该文件夹包含./bin/wine二进制文件。\n- wine压缩包(要求为 .tar.xz格式)也要放在`$TagFolder` 下,并且确保将其解压到同目录后,会出现`$WineFolder`。\n 多版本Wine共存 v2 + 环境设置 - 图形渲染设置 \ No newline at end of file diff --git a/patchapp/src/main/res/values/strings.xml b/patchapp/src/main/res/values/strings.xml index 2c560f60..da73d43e 100644 --- a/patchapp/src/main/res/values/strings.xml +++ b/patchapp/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ add function: custom resolution show cursor float action button\n- Custom location of drive D\n- Custom Control - custom resolution + container settings - custom resolution android 11+ soft-input no-crashing start patch install patched apk @@ -75,4 +75,5 @@ add function: multiwine v2 Added a download icon on the left-top of \"containers manager\" page, click to enter the \"add/remove wines\" page.\n- Local: Edit the downloaded or bundled Wines. After Clicking Install (extract) option, it will be displayed as \'active\', which can be selected when creating a new container. Use Uninstall (delete extracted folder) option to reduce local storage. Wines are stored at z:/opt/WineCollection.\n- Downloadable: Download all kinds of Wines from the Internet. Available sources are WineHQ(Official build, only ubuntu18-builds are listed) and Kron4ek(shrinked size, staging versions are not included). Downloaded Wines appear at \'Local\' page. \n\nhow to add bundled Wines into caches:\n`/opt/WineCollection/custom/$TagFolder/$WineFolder/bin/wine`\n- `$TagFolder` : the folder\'s name represents this wine\'s name. it shows when creating containers, should be unique.\n- `$WineFolder` : it should contains the `./bin/wine` binary file.\n- also in `$TagFolder` there should be a `.tar.xz` archive file, from which `$WineFolder` will be extracted. MultiWine v2 + container settings - renderer options \ No newline at end of file diff --git a/readme.md b/readme.md index eab8d04a..e30e3b15 100644 --- a/readme.md +++ b/readme.md @@ -34,10 +34,12 @@ - [自定义d盘路径](https://ewt45.github.io/blogs/2022/winter/exagearFab/driveD.html) - [自定义操作模式](https://www.bilibili.com/video/BV1fL41167Ji/) - [强制显示鼠标光标](https://ewt45.github.io/blogs/2022/winter/exagearDefaultCursor/) -- [自定义分辨率](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) +- [环境设置- 自定义分辨率](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) - [安卓11+调起输入法](https://ewt45.github.io/blogs/2022/autumn/exagearKeyboard/) - [手动选择obb](https://ewt45.github.io/blogs/2022/winter/exagearFindObb/) - exe快捷方式直接启动 +- 多版本wine共存 v2 +- 环境设置 - 渲染方式 ## 第三方依赖 @@ -51,6 +53,29 @@ ## 更新历史 +### v0.0.4 +- 新功能: +1. multiwine v2 可在线下载wine。在容器管理界面右上角新增下载按钮,点击进入wine版本管理界面。 + - “本地”页面:对已下载或预置的wine进行管理。点击安装(解压)后,会显示“已启用”,已启用的wine会显示在新建容器时的选项中。可通过卸载(删除已解压文件夹)来减少本地占用。本地存放位置为:z:/opt/WineCollection。 + - “可下载”页面:从网络下载更多版本的wine。下载源可选择WineHQ(官方构建)或Kron4ek(第三方,体积小),其中WineHQ仅提供 ubuntu 18 的对应列表,Kron4ek不提供 staging 版本。下载成功后会显示在“本地”页面。若由于网络原因下载失败,可以尝试切换下载线路。 + - 创建一个容器后,该容器对应wine路径存放在/home/xdroid_n/envp.txt中。 + - 如何在数据包中预置wine:一个wine二进制文件的路径:`/opt/WineCollection/custom/$TagFolder/$WineFolder/bin/wine` + - `$TagFolder` : 该文件夹名对应一个版本的wine名字,用于新建容器时显示。 + - `$WineFolder` : 该文件夹包含./bin/wine二进制文件。 + - wine压缩包(要求为 .tar.xz格式)也要放在`$TagFolder` 下,并且确保将其解压到同目录后,会出现`$WineFolder`。 + +2. 环境设置- 渲染方式 + - 多wine共存 v2 不包括 根据容器设置中的渲染方式设置动态链接库(libGL.so.1)路径的功能。 若apk之前存在选择渲染方式的功能,在升到多wine v2后,请使用ed自助补丁额外一添加添加 环境设置-渲染方式,以便继续支持选择渲染方式功能,注意添加渲染方式后,不同渲染的路径与之前有所不同,可以编辑apk中的dex,找到 ContainerSettingsFragment$renderEntries.smali 查看并自行修改。 + - 目前添加了6种渲染方式。程序会根据选择的渲染,设置不同的动态链接库路径。需要在指定位置分别放入不同渲染的libGL.so.1。与原先的切换渲染方式比较: + - 原先切换渲染:启动容器后,每次切换渲染,将libGL.so解压到 /usr/lib/i386-linux-gnu 覆盖原文件。 + - 现在切换渲染:将libGL.so放入不同的文件夹(如/opt/lib/vo, /opt/lib/tz) 中。每次切换渲染,进入容器设置页面切换选项,进入容器。各有利弊,请选择自己喜欢的方式。 + - 另外,某些渲染方式还会有额外的操作: + - virgl overlay: 自动添加参数 VTEST_WIN=1 VTEST_SOCK= + - VirGL_built_in: 通过java新建进程,自动运行 libvirgl_test_server.so(仅xegw的apk支持)。不需要Mcat和/opt/start.sh。 无需virgl overlay的启动服务。日志输出到Android/data/包名/logs/virglLog.txt。 + - virtio-gpu: 尝试启动Mcat。在xegw之前,Mcat用于启动proot环境,即无免termux使用该渲染。在第一版的xegw apk中,mcat被重写 设定为运行/opt/start.sh,用于启动virgl built-in,不会自动启动proot。 + - turnip dxvk: 自动添加参数 GALLIUM_DRIVER=zink MESA_VK_WSI_DEBUG=sw + - 原先启动mcat的方式为:dex中,在UBTLaunchConfigureation -> addArgumentsToEnvironment函数中,读取该容器渲染设置的值,并将其设置到GALLIUM_DRIVER= 环境变量。若值为virpipe则启动Mcat。现在由于值并非GALLIUM_DRIVER的有效值,所以建议删除UBTLaunchConfigureation中的相关代码。 + ### v0.0.3 - 修改的apk使用默认密钥签名,安装修改后的apk不再需要手动重新签名或卸载原有apk。 - 添加新功能:exe快捷方式直接启动 diff --git a/readme/readme_en.md b/readme/readme_en.md index 3ccbfc01..7141ed36 100644 --- a/readme/readme_en.md +++ b/readme/readme_en.md @@ -30,10 +30,12 @@ So I'm wondering if I can make an application. The user only needs to click a bu - Custom location of drive D - Custom Control - [show cursor](https://ewt45.github.io/blogs/2022/winter/exagearDefaultCursor/) -- [custom resolution](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) +- [container settings - custom resolution](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) - [android 11+ soft-input no-crashing](https://ewt45.github.io/blogs/2022/autumn/exagearKeyboard/) - [select obb manually](https://ewt45.github.io/blogs/2022/winter/exagearFindObb/) - launch exe shortcut directly +- MultiWine v2 +- container settings - renderer ## Third-party project dependencies @@ -46,6 +48,26 @@ So I'm wondering if I can make an application. The user only needs to click a bu ## Change Log + +### v0.0.4 + +- MultiWIne v2 :Added a download icon on the left-top of "containers manager" page, click to enter the "add/remove wines" page. + - Local: Edit the downloaded or bundled Wines. After Clicking Install (extract) option, it will be displayed as 'active', which can be selected when creating a new container. Use Uninstall (delete extracted folder) option to reduce local storage. Wines are stored at z:/opt/WineCollection. + - Downloadable: Download all kinds of Wines from the Internet. Available sources are WineHQ(Official build, only ubuntu18-builds are listed) and Kron4ek(shrinked size, staging versions are not included). Downloaded Wines appear at 'Local' page. + - how to add bundled Wines into caches: wine binary file should appear at `/opt/WineCollection/custom/$TagFolder/$WineFolder/bin/wine` + - `$TagFolder` : the folder's name represents this wine's name. it shows when creating containers, should be unique. + - `$WineFolder` : it should contains the `./bin/wine` binary file. + - also in `$TagFolder` there should be a `.tar.xz` archive file, from which `$WineFolder` will be extracted. +- container settings - renderer : + - the renderer option is separated from multiwine now. multiwine v2 alone doesn't have the ability to set environment variables by renderer options in container settings. you need to add the function separately from edpatch. + - what render options do: + - each renderer has a diffrent path for libraries e.g.libGL.so.1, this path will be added as LD_LIBRARY_PATH=xxx so that you don't need to replace it at /usr/lib/i386-linux-gnu everytime. If you want to change this path, go to apk's dex -> ContainerSettingsFragment$renderEntries.smali + - virgl overlay: add VTEST_WIN=1 VTEST_SOCK= + - VirGL_built_in: run libvirgl_test_server.so from java process (without need of Mcat and /opt/start.sh) , logs output at /sdcard/virglLog.txt + - virtio-gpu: try to start Mcat, which used to launch a proot environment for this renderer, but xegw replaces the old mcat so it won't work unless you put the old mcat.so back. + - turnip dxvk: add GALLIUM_DRIVER=zink MESA_VK_WSI_DEBUG=sw + +### v0.0.3 - Now Sign apks with default signature. No need to uninstall original one or resign the patched apk manually , but it may be recognized as virus. - New functions available: launch exe shortcut directly - Long press the app icon, select an exe shortcut, and click to run it. diff --git a/readme/readme_ru.md b/readme/readme_ru.md index 9b23e461..1a910788 100644 --- a/readme/readme_ru.md +++ b/readme/readme_ru.md @@ -29,10 +29,12 @@ - Выбор локации диска D - Кастомное управление - [Отображать курсор](https://ewt45.github.io/blogs/2022/winter/exagearDefaultCursor/) -- [Выбор разрешений экрана](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) +- [Настройки контейнера - Кастомное разрешение](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) - [Исправление работы клавиатуры на android 11+](https://ewt45.github.io/blogs/2022/autumn/exagearKeyboard/) - [Выбор файла obb](https://ewt45.github.io/blogs/2022/winter/exagearFindObb/) - Добавить ярлыки .exe на рабочий стол Android +- MultiWine v2 +- Настройки контейнера - Выбор рендера ## Third-party project dependencies - [apktool](https://ibotpeaches.github.io/Apktool/) @@ -47,6 +49,25 @@ ## Change Log +### v0.0.4 + +- MultiWine v2: Добавляется значок загрузки необходимой версии wine в левом верхнем углу страницы "создание контейнеров", тапните на вкладку "добавление/удаление wine". + - Установка wine: Вы можете редактировать загруженные или предустановленные версии wine. После выбора опции Установить (Извлечь) выбранная версия wine будет отображаться как 'Активная', теперь её можно выбрать при создании нового контейнера. Используйте опцию Удалить (удалить папку с wine), чтобы уменьшить объем занятой внутренней памяти вашего девайса. Файлы wine хранятся в папке Z:/opt/WineCollection. + - Загрузка файлов wine: загрузка всех видов wine из интернета. Доступные источники: WineHQ (официальная сборка, перечислены только сборки ubuntu18) и Kron4ek (уменьшенный размер, промежуточные версии wine не включены). Загруженные версии wine появляются на странице 'Установленные'. + - Как добавить предустановленные версии wine в кэши: `/opt/WineCollection/custom/$TagFolder/$WineFolder/bin/wine` + - `$TagFolder` : имя папки необходимого wine, оно отображается при создании контейнеров, и должно быть уникальным. + - `$WineFolder`: он должен содержать двоичный файл `./bin/wine`. + - так же в `$TagFolder` должен быть `.tar.xz` архивный файл, из которого будет извлечена `$WineFolder`. + +- Настройки контейнера - Выбор рендера + - the renderer option is separated from multiwine now. multiwine v2 alone doesn't have the ability to set environment variables by renderer options in container settings. you need to add the function separately from edpatch. + - what render options do: + - each renderer has a diffrent path for libraries e.g.libGL.so.1, this path will be added as LD_LIBRARY_PATH=xxx so that you don't need to replace it at /usr/lib/i386-linux-gnu everytime. If you want to change this path, go to apk's dex -> ContainerSettingsFragment$renderEntries.smali + - virgl overlay: add VTEST_WIN=1 VTEST_SOCK= + - VirGL_built_in: run libvirgl_test_server.so from java process (without need of Mcat and /opt/start.sh) , logs output at /sdcard/virglLog.txt + - virtio-gpu: try to start Mcat, which used to launch a proot environment for this renderer, but xegw replaces the old mcat so it won't work unless you put the old mcat.so back. + - turnip dxvk: add GALLIUM_DRIVER=zink MESA_VK_WSI_DEBUG=sw + ### v0.0.3 - Использовать подпись по умолчанию, нет необходимости удалять установленный apk или переподписывать apk вручную. Но он может быть распознан как вирус. - New functions available: Добавить ярлыки .exe на рабочий стол Android