Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified app/src/main/assets/redirect.tzst
Binary file not shown.
22 changes: 2 additions & 20 deletions app/src/main/java/app/gamenative/service/DownloadService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package app.gamenative.service

import android.content.Context
import android.os.Environment
import app.gamenative.PrefManager
import app.gamenative.utils.StorageUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -38,27 +37,10 @@ object DownloadService {
baseExternalAppDirPath = extFiles?.parentFile?.path ?: ""

val sm = context.getSystemService(android.os.storage.StorageManager::class.java)
val appFilesDirs = StorageUtils.getAllExternalFilesDirs(context)
externalVolumePaths = StorageUtils.getAllExternalFilesDirs(context)
.filter { Environment.getExternalStorageState(it) == Environment.MEDIA_MOUNTED }
.filter { sm?.getStorageVolume(it)?.isPrimary != true }
// both layouts per volume: legacy Android/data (existing installs) + public root (new installs)
externalVolumePaths = appFilesDirs
.flatMap { dir -> listOfNotNull(dir.absolutePath, StorageUtils.publicInstallRoot(dir)?.absolutePath) }
.distinct()

migrateExternalStoragePath()
}

// Android/data paths pay a ~1000x FUSE metadata penalty (MediaProvider disables kernel
// caching there); repoint the install pref at the public root so new installs avoid it
private fun migrateExternalStoragePath() {
val pref = PrefManager.externalStoragePath
if (pref.isBlank() || !pref.contains("/Android/data/")) return
val public = StorageUtils.publicInstallRoot(File(pref)) ?: return
if (StorageUtils.ensureInstallRoot(public)) {
Timber.i("Migrating external install root from $pref to ${public.absolutePath}")
PrefManager.externalStoragePath = public.absolutePath
}
.map { it.absolutePath }
}

@Synchronized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,18 +561,16 @@ fun SettingsGroupInterface(
useExternalStorage = it
PrefManager.useExternalStorage = it
if (it && dirs.isNotEmpty()) {
PrefManager.externalStoragePath = StorageUtils.preferredInstallRoot(dirs[0])
PrefManager.externalStoragePath = dirs[0].absolutePath
}
},
)
if (useExternalStorage) {
// Currently selected item
var selectedIndex by rememberSaveable {
mutableStateOf(
dirs.indexOfFirst { dir ->
dir.absolutePath == PrefManager.externalStoragePath ||
StorageUtils.publicInstallRoot(dir)?.absolutePath == PrefManager.externalStoragePath
}.takeIf { it >= 0 } ?: 0,
dirs.indexOfFirst { it.absolutePath == PrefManager.externalStoragePath }
.takeIf { it >= 0 } ?: 0,
)
}
SettingsListDropdown(
Expand All @@ -581,7 +579,7 @@ fun SettingsGroupInterface(
value = selectedIndex,
onItemSelected = { idx ->
selectedIndex = idx
PrefManager.externalStoragePath = StorageUtils.preferredInstallRoot(dirs[idx])
PrefManager.externalStoragePath = dirs[idx].absolutePath
},
colors = settingsTileColorsAlt(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3667,12 +3667,6 @@ private fun setupXEnvironment(
envVars.remove("DXVK_FRAME_RATE")
envVars.remove("VKD3D_FRAME_RATE")
if (!envVars.has("WINEESYNC")) envVars.put("WINEESYNC", "1")

val ffpGameDir = runCatching {
File(SteamService.getAppDirPath(ContainerUtils.extractGameIdFromContainerId(appId))).canonicalFile.path
}.getOrDefault("")
if (ffpGameDir.startsWith("/storage/")) envVars.put("FFP_ENABLE", "1")

val graphicsDriverConfig = KeyValueSet(container.getGraphicsDriverConfig())
if (graphicsDriverConfig.get("version").lowercase(Locale.getDefault()).contains("gen8")) {
var tuDebug = envVars.get("TU_DEBUG")
Expand Down
8 changes: 3 additions & 5 deletions app/src/main/java/app/gamenative/utils/ContainerUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1043,13 +1043,11 @@ object ContainerUtils {
}
}

val resolvedGameFolderPath = StorageUtils.migrateLegacyGameDir(gameFolderPath)

if (resolvedGameFolderPath != null) {
if (gameFolderPath != null) {
// Check if A: drive is already mapped to the correct path
var hasCorrectADrive = false
for (drive in Container.drivesIterator(container.drives)) {
if (drive[0] == "A" && drive[1] == resolvedGameFolderPath) {
if (drive[0] == "A" && drive[1] == gameFolderPath) {
hasCorrectADrive = true
break
}
Expand All @@ -1060,7 +1058,7 @@ object ContainerUtils {
val currentDrives = container.drives
// Rebuild drives string, excluding existing A: drive and adding new one
val drivesBuilder = StringBuilder()
drivesBuilder.append("A:$resolvedGameFolderPath")
drivesBuilder.append("A:$gameFolderPath")

// Add all other drives (excluding A:)
for (drive in Container.drivesIterator(currentDrives)) {
Expand Down
49 changes: 0 additions & 49 deletions app/src/main/java/app/gamenative/utils/StorageUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,55 +98,6 @@ object StorageUtils {
return result
}

private const val PUBLIC_INSTALL_DIR_NAME = "GameNative"

/**
* Maps an app-specific dir (<volume>/Android/data/<pkg>/files) to a public install root
* (<volume>/GameNative). MediaProvider disables FUSE kernel caching under Android/data,
* making per-open metadata ops ~1000x slower there; public dirs get normal dcache treatment.
*/
fun publicInstallRoot(appFilesDir: File): File? {
val path = appFilesDir.absolutePath
val idx = path.indexOf("/Android/data/")
if (idx <= 0) return null
return File(path.substring(0, idx), PUBLIC_INSTALL_DIR_NAME)
}

fun ensureInstallRoot(dir: File): Boolean {
if (!dir.isDirectory && !dir.mkdirs()) return false
runCatching { File(dir, ".nomedia").createNewFile() }
return true
}

fun preferredInstallRoot(appFilesDir: File): String {
val public = publicInstallRoot(appFilesDir)
if (public != null && ensureInstallRoot(public)) return public.absolutePath
return appFilesDir.absolutePath
}

fun migrateLegacyGameDir(path: String?): String? {
if (path.isNullOrBlank()) return path
val idx = path.indexOf("/Android/data/")
if (idx <= 0) return path
val filesIdx = path.indexOf("/files/", idx)
if (filesIdx < 0) return path
val legacyRoot = File(path.substring(0, filesIdx + "/files".length))
val rel = path.substring(filesIdx + "/files/".length)
val src = File(path)
if (!src.isDirectory) return path
val publicRoot = publicInstallRoot(legacyRoot) ?: return path
val dst = File(publicRoot, rel)
if (dst.exists() || !ensureInstallRoot(publicRoot)) return path
dst.parentFile?.mkdirs()
return if (src.renameTo(dst)) {
Timber.i("Migrated game dir $path to ${dst.absolutePath}")
dst.absolutePath
} else {
Timber.w("Could not migrate $path; leaving in place")
path
}
}

/**
* Gets all app-specific external files directories, using StorageManager as a fallback
* for cases where context.getExternalFilesDirs(null) might return null or incomplete results
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import java.util.concurrent.atomic.AtomicLong;

public abstract class ImageFsInstaller {
public static final byte LATEST_VERSION = 29;
public static final byte LATEST_VERSION = 28;

private static void resetContainerImgVersions(Context context) {
ContainerManager manager = new ContainerManager(context);
Expand Down
Binary file modified app/src/modern/assets/libredirect-bionic-wx.so
Binary file not shown.
Loading