diff --git a/.gitignore b/.gitignore index 7e71a9864..ecf01c18a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ signing.properties References/ *.hprof android_sysvshm/build64/ + +# local build artifacts (FEX/Proton wcp) +dist/ diff --git a/UNIXLIBS_README.md b/UNIXLIBS_README.md new file mode 100644 index 000000000..c43c0045c --- /dev/null +++ b/UNIXLIBS_README.md @@ -0,0 +1,96 @@ +# FEX UnixLibs for WinNative — What It Is & How It Works + +A short guide to the new **UnixLibs** feature: what changed, what gets installed, +and how the new `.so` files get loaded. + +--- + +## The one-paragraph version + +FEX (the x86→ARM translator used by ARM64EC containers) always ran as **Windows +DLLs living inside the container's C: drive**. Those DLLs are "boxed" inside +Wine's Windows world, so they can't talk to the Android kernel directly. +**UnixLibs** adds a small **native `.so`** that runs on the *Android side* and +gives FEX a direct line to the kernel — unlocking hardware-assisted x86 memory +ordering (TSO) and other speedups. Nothing about how you use containers changes; +it just runs faster where the hardware supports it. + +--- + +## How it works (simple) + +1. The FEX DLLs (`libwow64fex.dll` / `libarm64ecfex.dll`) stay in `system32` — + they're still the entry point Wine loads. +2. On launch, each FEX DLL asks Wine: *"load my native helper by name."* +3. Wine finds the matching **`.so`** (`libwow64fex.so` / `libarm64ecfex.so`) in + the running Wine/Proton's Unix library folder and loads it. +4. FEX now has a bridge from the boxed Windows side out to the Android kernel and + uses it for the fast paths (see performance note at the bottom). + +The DLL and the `.so` are a **pair** — the DLL loads the `.so`; they work +*together*, the `.so` does not replace the DLL. + +--- + +## What's different from the old setup + +| Piece | Old | New (UnixLibs) | +|---|---|---| +| **Proton** | Wine couldn't load a `.so` helper by name, so FEX silently fell back to the slower in-DLL path. | Proton's `ntdll` now supports **load-unixlib-by-name**, so it can find and load the `.so`. | +| **FEX version** | The FEX `.wcp` shipped **only** the two PE `.dll` files. | The FEX `.wcp` ships the `.dll` files **plus** the matching native **`.so`** files (built for Android/Bionic). | +| **WinNative APK** | Only copied the FEX `.dll` into `system32`. | Adds a **"Use UnixLibs" toggle** and, when on, copies the `.so` into the active Proton's Unix lib folder so Wine can find it. | + +--- + +## What gets installed now + +Three matching pieces are needed (all ARM64EC only): + +1. **New Proton** — e.g. `Proton-11-B5-Arm64EC-Steam-UnixLibs` (its `ntdll` + knows how to load the `.so`). +2. **New FEX component** — e.g. `FEX-2607-UnixLibs`, which contains: + - `libwow64fex.dll`, `libarm64ecfex.dll` → copied into `system32` (as before) + - `libwow64fex.so`, `libarm64ecfex.so` → the new native helpers +3. **New WinNative APK** — with the **Use UnixLibs** toggle and the wiring that + places the `.so` where Wine looks for it. + +Install the Proton and FEX components from **Components** like normal, pick them +in the container/shortcut, and make sure **Use UnixLibs** is on (it turns on +automatically when the selected FEX version includes `.so` files). + +--- + +## How the `.so` files get loaded + +- When you launch a container, WinNative copies the FEX `.so` files into the + **active Proton's** `lib/wine/aarch64-unix/` folder (the exact place Wine + searches). This happens per-launch, so it follows whichever Proton you pick. +- Wine's `ntdll` then loads the `.so` by name when FEX asks for it. +- Turning the toggle **off** removes the `.so` again, so FEX goes back to the + plain DLL path. + +**How to confirm it worked:** enable FEX/emulator logs, launch a game, and look +in the FEX log for: + +``` +FEX: Loaded FEXUnixLib +``` + +If you see that (once for each backend), the native helper is active. If it's +missing, FEX is running the old DLL-only path. + +--- + +## What you might feel (performance) + +FEX already translated x86 to native ARM either way — UnixLibs doesn't make +translation "native," it gives FEX **kernel access**. The main win is +**hardware TSO memory ordering**: FEX can drop the expensive software memory +barriers it normally needs for correct x86 behavior and let the CPU enforce it +instead — *fast and correct at the same time* (the same trick Apple's Rosetta +uses). Expect the biggest gains in memory- and multithread-heavy games, plus +smoother frame pacing. + +Caveat: this is **hardware/kernel dependent**. On chips/kernels that expose the +memory-model toggle you'll feel it; on ones that don't, the `.so` still loads but +that speedup isn't available — no gain, but no regression either. diff --git a/app/src/main/feature/library/GameSettings.kt b/app/src/main/feature/library/GameSettings.kt index 85aae8e63..73152df97 100644 --- a/app/src/main/feature/library/GameSettings.kt +++ b/app/src/main/feature/library/GameSettings.kt @@ -507,6 +507,8 @@ class GameSettingsStateHolder { val selectedFexcoreVersion = mutableIntStateOf(0) val fexcorePresetEntries = mutableStateOf>(emptyList()) val selectedFexcorePreset = mutableIntStateOf(0) + val fexcoreUnixLibsFlags = mutableStateOf>(emptyList()) + val useUnixLibs = mutableStateOf(true) // Advanced - System val startupSelectionEntries = mutableStateOf>(emptyList()) @@ -3554,6 +3556,18 @@ private fun AdvancedSection( // FEXCore — hidden when FEXCore isn't explicitly in either slot. if (state.showFexcoreFrame.value) { + val fexUnixCapable = state.fexcoreUnixLibsFlags.value + .getOrNull(state.selectedFexcoreVersion.intValue) ?: false + SettingGroup { + SettingSwitch( + label = stringResource(R.string.use_unix_libs), + checked = state.useUnixLibs.value && fexUnixCapable, + onCheckedChange = { state.useUnixLibs.value = it }, + enabled = fexUnixCapable + ) + } + Spacer(Modifier.height(SettingSectionGap)) + val fexcoreUsage = emulatorUsageLabel(state, setOf("fexcore")) EmulatorSectionHeader(stringResource(R.string.container_fexcore_config), fexcoreUsage) Spacer(Modifier.height(8.dp)) diff --git a/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt b/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt index c6757926b..0192754bc 100644 --- a/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt +++ b/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt @@ -422,6 +422,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( } state.fullscreenStretched.value = c?.isFullscreenStretched() ?: false + state.useUnixLibs.value = c?.isUseUnixLibs() ?: true // Steam fields are shortcut-only in the UI; leave any existing steam // state on the container untouched — saveSettings() skips them. @@ -799,6 +800,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( c.setWinComponents(wincomponents) c.setDrives(drivesString) c.setFullscreenStretched(state.fullscreenStretched.value) + c.setUseUnixLibs(state.useUnixLibs.value) c.setInputType(finalInputType) c.setExclusiveXInput(state.containerExclusiveInput.value) c.setStartupSelection(startupSelection) @@ -839,6 +841,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( data.put("wincomponents", wincomponents) data.put("drives", drivesString) data.put("fullscreenStretched", state.fullscreenStretched.value) + data.put("useUnixLibs", state.useUnixLibs.value) data.put("inputType", finalInputType) data.put("exclusiveXInput", state.containerExclusiveInput.value) data.put("startupSelection", startupSelection.toInt()) @@ -989,6 +992,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( if (firstDash >= 0) items.add(entryName.substring(firstDash + 1)) } state.fexcoreVersionEntries.value = items + state.fexcoreUnixLibsFlags.value = items.map { contentsManager.fexcoreVersionHasUnixLibs(it) } val saved = container?.getFEXCoreVersion() ?: "" selectByValue(items, saved, state.selectedFexcoreVersion) } diff --git a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt index 0fa0f52ba..d685cf306 100644 --- a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt +++ b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt @@ -414,6 +414,10 @@ class ShortcutSettingsComposeDialog private constructor( if (container.isFullscreenStretched) "1" else "0" ) state.fullscreenStretched.value = fullscreenStretched == "1" + state.useUnixLibs.value = getShortcutSetting( + "useUnixLibs", + if (container.isUseUnixLibs) "1" else "0" + ) == "1" // LC_ALL state.lcAll.value = getShortcutSetting("lc_all", container.getLC_ALL()) @@ -824,6 +828,7 @@ class ShortcutSettingsComposeDialog private constructor( } state.fexcoreVersionEntries.value = items + state.fexcoreUnixLibsFlags.value = items.map { contentsManager.fexcoreVersionHasUnixLibs(it) } val savedVersion = if (shouldUseShortcutOverrides(container)) getShortcutSetting("fexcoreVersion", container.getFEXCoreVersion()) else @@ -1071,6 +1076,13 @@ class ShortcutSettingsComposeDialog private constructor( if (container.isFullscreenStretched) "1" else "0" ) + // Use UnixLibs + hasContainerOverride = hasContainerOverride or saveOverride( + "useUnixLibs", + if (state.useUnixLibs.value) "1" else "0", + if (container.isUseUnixLibs) "1" else "0" + ) + // Win components val wincomponents = buildWinComponentsString() hasContainerOverride = @@ -2160,6 +2172,7 @@ class ShortcutSettingsComposeDialog private constructor( state.lcAll.value = container.getLC_ALL() state.fullscreenStretched.value = container.isFullscreenStretched + state.useUnixLibs.value = container.isUseUnixLibs val startupEntries = state.startupSelectionEntries.value state.selectedStartupSelection.intValue = container.getStartupSelection().toInt() diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 232fde91a..4f146e6a8 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -505,6 +505,7 @@ FEXCore FEXCore-forudindstilling FEXCore-version + Brug UnixLibs Grafikdriver diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 54ef2714d..3db58b93d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -505,6 +505,7 @@ FEXCore FEXCore-Voreinstellung FEXCore-Version + UnixLibs verwenden Grafiktreiber diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index d5c56ec06..f173826f6 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -505,6 +505,7 @@ FEXCore Preset de FEXCore Versión de FEXCore + Usar UnixLibs Controlador gráfico diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 52d81280e..15d7a9801 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -505,6 +505,7 @@ FEXCore Préréglage FEXCore Version de FEXCore + Utiliser les UnixLibs Pilote graphique diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index a285325bb..73340af82 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -559,6 +559,7 @@ FEXCore FEXCore प्रीसेट FEXCore संस्करण + UnixLibs का उपयोग करें ग्राफ़िक्स ड्राइवर ग्राफ़िक्स ड्राइवर कॉन्फ़िगरेशन ग्राफ़िक्स ड्राइवर संस्करण diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 21d6b2bc9..8ce77f6ba 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -505,6 +505,7 @@ FEXCore Preset FEXCore Versione FEXCore + Usa UnixLibs Driver grafico diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 1d82ebedf..d3e12a025 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -505,6 +505,7 @@ FEXCore FEXCore 프리셋 FEXCore 버전 + UnixLibs 사용 그래픽 드라이버 diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index cb133e128..98613951f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -509,6 +509,7 @@ FEXCore Preset FEXCore Wersja FEXCore + Użyj UnixLibs Sterownik grafiki diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 996104f88..7bd36eee2 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -505,6 +505,7 @@ FEXCore Predefinicao FEXCore Versao do FEXCore + Usar UnixLibs Driver Grafico diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 0ec0c94c8..8411c5bc1 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -505,6 +505,7 @@ FEXCore Preset FEXCore Versiune FEXCore + Folosește UnixLibs Driver grafic diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f64eb6a8a..392f3549a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -513,6 +513,7 @@ FEXCore Пресет FEXCore Версия FEXCore + Использовать UnixLibs Графический драйвер Конфигурация графического драйвера diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 281acaeb0..45c92c010 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -509,6 +509,7 @@ FEXCore Пресет FEXCore Версія FEXCore + Використовувати UnixLibs Графічний драйвер diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 682ac8177..20e4aec3e 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -505,6 +505,7 @@ FEXCore FEXCore 预设 FEXCore 版本 + 使用 UnixLibs 图形驱动 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 1dc93cd8f..90001b4e8 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -505,6 +505,7 @@ FEXCore FEXCore 預設 FEXCore 版本 + 使用 UnixLibs 圖形驅動程式 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 540cc9451..bf004118b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -585,6 +585,7 @@ FEXCore FEXCore Preset FEXCore Version + Use UnixLibs Graphics Driver diff --git a/app/src/main/runtime/container/Container.java b/app/src/main/runtime/container/Container.java index 96e6812b1..8f259419a 100644 --- a/app/src/main/runtime/container/Container.java +++ b/app/src/main/runtime/container/Container.java @@ -51,6 +51,7 @@ public class Container { private String drives = DEFAULT_DRIVES; private String wineVersion = WineInfo.MAIN_WINE_VERSION.identifier(); private boolean fullscreenStretched; + private boolean useUnixLibs = true; private byte startupSelection = STARTUP_SELECTION_ESSENTIAL; private String cpuList; private String cpuListWoW64; @@ -207,6 +208,10 @@ public void setLC_ALL(String lc_all) { public void setFullscreenStretched(boolean fullscreenStretched) { this.fullscreenStretched = fullscreenStretched; } + public boolean isUseUnixLibs() { return useUnixLibs; } + + public void setUseUnixLibs(boolean useUnixLibs) { this.useUnixLibs = useUnixLibs; } + public byte getStartupSelection() { return startupSelection; } @@ -424,6 +429,7 @@ public void saveData() { data.put("wincomponents", wincomponents); data.put("drives", drives); data.put("fullscreenStretched", fullscreenStretched); + data.put("useUnixLibs", useUnixLibs); data.put("inputType", inputType); data.put("exclusiveXInput", exclusiveXInput); data.put("startupSelection", startupSelection); @@ -508,6 +514,9 @@ public void loadData(JSONObject data) throws JSONException { case "fullscreenStretched" : setFullscreenStretched(data.getBoolean(key)); break; + case "useUnixLibs" : + setUseUnixLibs(data.getBoolean(key)); + break; case "inputType" : setInputType(data.getInt(key)); break; diff --git a/app/src/main/runtime/content/ContentsManager.java b/app/src/main/runtime/content/ContentsManager.java index 2c85adea3..c9d22b057 100644 --- a/app/src/main/runtime/content/ContentsManager.java +++ b/app/src/main/runtime/content/ContentsManager.java @@ -17,6 +17,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import org.json.JSONArray; import org.json.JSONException; @@ -750,6 +751,69 @@ public ContentProfile getProfileByEntryName(String entryName) { return null; } + public boolean profileHasUnixLibs(ContentProfile profile) { + if (profile == null) return false; + return dirContainsSharedObject(getInstallDir(context, profile)); + } + + public boolean fexcoreVersionHasUnixLibs(String fexcoreVersion) { + if (fexcoreVersion == null || fexcoreVersion.isEmpty()) return false; + return profileHasUnixLibs(getProfileByEntryName("fexcore-" + fexcoreVersion)); + } + + private static boolean dirContainsSharedObject(File dir) { + if (dir == null) return false; + File[] files = dir.listFiles(); + if (files == null) return false; + for (File file : files) { + if (file.isDirectory()) { + if (dirContainsSharedObject(file)) return true; + } else if (isSharedObject(file.getName())) { + return true; + } + } + return false; + } + + private static boolean isSharedObject(String name) { + String lower = name.toLowerCase(Locale.ROOT); + return lower.endsWith(".so") || lower.contains(".so."); + } + + public void removeAppliedUnixLibs(ContentProfile profile) { + if (profile == null || profile.fileList == null) return; + for (ContentProfile.ContentFile contentFile : profile.fileList) { + if (!isSharedObject(new File(contentFile.target).getName())) continue; + File targetFile = new File(getPathFromTemplate(contentFile.target)); + if (targetFile.exists() && targetFile.delete()) { + Log.i("ContentsManager", "UnixLibs: removed " + targetFile.getName()); + } + } + } + + public void copyUnixLibsToDir(ContentProfile profile, File destDir) { + if (profile == null || profile.fileList == null) return; + for (ContentProfile.ContentFile contentFile : profile.fileList) { + String name = new File(contentFile.target).getName(); + if (!isSharedObject(name)) continue; + File sourceFile = new File(getInstallDir(context, profile), contentFile.source); + if (!sourceFile.exists()) continue; + File destFile = new File(destDir, name); + FileUtils.copy(sourceFile, destFile); + FileUtils.chmod(destFile, 0771); + } + } + + public void deleteUnixLibsFromDir(ContentProfile profile, File destDir) { + if (profile == null || profile.fileList == null) return; + for (ContentProfile.ContentFile contentFile : profile.fileList) { + String name = new File(contentFile.target).getName(); + if (!isSharedObject(name)) continue; + File destFile = new File(destDir, name); + if (destFile.exists()) destFile.delete(); + } + } + public boolean applyContent(ContentProfile profile) { if (profile.type != ContentProfile.ContentType.CONTENT_TYPE_WINE && profile.type != ContentProfile.ContentType.CONTENT_TYPE_PROTON) { @@ -760,7 +824,8 @@ public boolean applyContent(ContentProfile profile) { targetFile.delete(); FileUtils.copy(sourceFile, targetFile); - if (profile.type == ContentProfile.ContentType.CONTENT_TYPE_BOX64) { + if (profile.type == ContentProfile.ContentType.CONTENT_TYPE_BOX64 + || isSharedObject(targetFile.getName())) { FileUtils.chmod(targetFile, 0771); } } diff --git a/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java b/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java index 58ac5f6ae..9a79fffe9 100644 --- a/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java +++ b/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java @@ -55,6 +55,7 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { private final Shortcut shortcut; private File workingDir; private Runnable preUnpackCallback; + private boolean fexUnixLibsActive = false; public static File ensureImageFsNativeLibrary( Context context, ImageFs imageFs, String libraryName) { @@ -371,11 +372,13 @@ private void extractEmulatorsDlls() { if (wowbox64Version == null) wowbox64Version = ""; if (fexcoreVersion == null) fexcoreVersion = ""; + boolean unixLibsPref = container.isUseUnixLibs(); if (shortcut != null) { emulator = shortcut.getSettingExtra("emulator", emulator); emulator64 = shortcut.getSettingExtra("emulator64", emulator64); wowbox64Version = shortcut.getSettingExtra("box64Version", wowbox64Version); fexcoreVersion = shortcut.getSettingExtra("fexcoreVersion", fexcoreVersion); + unixLibsPref = "1".equals(shortcut.getSettingExtra("useUnixLibs", unixLibsPref ? "1" : "0")); } boolean usesWowbox64 = emulator.equalsIgnoreCase("wowbox64"); @@ -384,6 +387,9 @@ private void extractEmulatorsDlls() { || emulator64.equalsIgnoreCase("fexcore") || !usesWowbox64; + fexUnixLibsActive = + usesFexcore && unixLibsPref && contentsManager.fexcoreVersionHasUnixLibs(fexcoreVersion); + Log.i( "GuestProgramLauncherComponent", "Launch runtime selected: emulator=" @@ -439,30 +445,44 @@ private void extractEmulatorsDlls() { "WowBox64 already loaded for launch: version=" + wowbox64Version); } - if (usesFexcore - && (fexcoreDllsMissing || !fexcoreVersion.equals(container.getExtra("fexcoreVersion")))) { - if (fexcoreVersion.isEmpty()) { - Log.w("GuestProgramLauncherComponent", "No FEXCore version selected; skipping content extraction"); - } else { - ContentProfile profile = contentsManager.getProfileByEntryName("fexcore-" + fexcoreVersion); - if (profile != null) { + if (usesFexcore) { + ContentProfile profile = + fexcoreVersion.isEmpty() ? null : contentsManager.getProfileByEntryName("fexcore-" + fexcoreVersion); + String wantMode = fexUnixLibsActive ? "unixlibs" : "dll"; + boolean modeChanged = !wantMode.equals(container.getExtra("fexcoreMode")); + boolean versionChanged = !fexcoreVersion.equals(container.getExtra("fexcoreVersion")); + boolean needsExtraction = + versionChanged || modeChanged || (!fexUnixLibsActive && fexcoreDllsMissing); + + if (needsExtraction) { + if (fexcoreVersion.isEmpty()) { + Log.w("GuestProgramLauncherComponent", "No FEXCore version selected; skipping content extraction"); + } else if (profile != null) { Log.i( "GuestProgramLauncherComponent", - "Loading FEXCore content profile: version=" + fexcoreVersion); + "Loading FEXCore content profile: version=" + fexcoreVersion + " mode=" + wantMode); contentsManager.applyContent(profile); + if (!fexUnixLibsActive) contentsManager.removeAppliedUnixLibs(profile); } else { Log.w( "GuestProgramLauncherComponent", "FEXCore content profile not installed; no bundled FEXCore archive will be loaded: version=" + fexcoreVersion); } + container.putExtra("fexcoreVersion", fexcoreVersion); + container.putExtra("fexcoreMode", wantMode); + containerDataChanged = true; + } else { + Log.i( + "GuestProgramLauncherComponent", + "FEXCore already loaded for launch: version=" + fexcoreVersion + " mode=" + wantMode); + } + + if (profile != null) { + File wineUnixDir = new File(environment.getImageFs().getWinePath(), "lib/wine/aarch64-unix"); + if (fexUnixLibsActive) contentsManager.copyUnixLibsToDir(profile, wineUnixDir); + else contentsManager.deleteUnixLibsFromDir(profile, wineUnixDir); } - container.putExtra("fexcoreVersion", fexcoreVersion); - containerDataChanged = true; - } else if (usesFexcore) { - Log.i( - "GuestProgramLauncherComponent", - "FEXCore already loaded for launch: version=" + fexcoreVersion); } if (containerDataChanged) container.saveData(); }