Skip to content
Open
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
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ dependencies {
// Room Database
implementation(libs.bundles.room)

// 7Zip binding
implementation(libs.sevenzipjbinding)

// Memory Leak Detection
// debugImplementation("com.squareup.leakcanary:leakcanary-android:3.0-alpha-8")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,12 @@ fun XServerScreen(
contentsManager,
onExtractFileListener,
)

if (container.wineVersion.lowercase().contains("proton-10")) {
// Only proton 10 can apply this fix
XServerScreenUtils.replaceXAudioDllsFromRedistributable(context, appId)
}

extractArm64ecInputDLLs(context, container) // REQUIRED: Uses updated xinput1_3 main.c from x86_64 build, prevents crashes with 3+ players, avoids need for input shim dlls.
extractx86_64InputDlls(context, container)
extractGraphicsDriverFiles(
Expand Down
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add this as a preinstallstep like we do with the other common redists?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the dlls have to be always replaced just before game start as changing proton will override the xaudio, which the preinstallstep may only run once.

Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package app.gamenative.ui.screen.xserver

import android.content.Context
import app.gamenative.data.GameSource
import app.gamenative.service.SteamService
import app.gamenative.service.amazon.AmazonService
import app.gamenative.service.epic.EpicService
import app.gamenative.service.gog.GOGService
import app.gamenative.utils.ContainerUtils
import app.gamenative.utils.CustomGameScanner
import app.gamenative.utils.FileUtils
import com.winlator.xenvironment.ImageFs
import net.sf.sevenzipjbinding.ExtractAskMode
import net.sf.sevenzipjbinding.ExtractOperationResult
import net.sf.sevenzipjbinding.IArchiveExtractCallback
import net.sf.sevenzipjbinding.IInArchive
import net.sf.sevenzipjbinding.ISequentialOutStream
import net.sf.sevenzipjbinding.SevenZip
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.io.RandomAccessFile

class XServerScreenUtils {
/**
* Replace DLLs from DirectX Redistributable
*/
companion object {
fun replaceXAudioDllsFromRedistributable(context: Context, appId: String) {
val gameSource = ContainerUtils.extractGameSourceFromContainerId(appId)
val appDirPath = try {
when (gameSource) {
GameSource.STEAM -> {
val gameId = ContainerUtils.extractGameIdFromContainerId(appId)
SteamService.getAppDirPath(gameId)
}
GameSource.GOG -> GOGService.getInstallPath(appId)
GameSource.EPIC -> {
val gameId = ContainerUtils.extractGameIdFromContainerId(appId)
EpicService.getInstallPath(gameId)
}
GameSource.AMAZON -> AmazonService.getInstallPath(appId)
GameSource.CUSTOM_GAME -> CustomGameScanner.getFolderPathFromAppId(appId)
}
} catch (e: Exception) {
Timber.tag("replaceXAudioDllsFromRedistributable")
.w(e, "Failed to resolve install path for appId=%s source=%s", appId, gameSource)
null
}

// Not Support Type
if (appDirPath == null) {
return
}

val appDir = File(appDirPath)

// Check the common path first, otherwise scan the game dir for DXSETUP.exe
var directXDir = File(appDirPath, "_CommonRedist/DirectX")
if (!directXDir.exists()) {
val dxSetupFile = FileUtils.findFilesRecursive(
rootPath = appDir.toPath(),
pattern = "DXSETUP.exe",
maxDepth = 5,
).findFirst().orElse(null)

if (dxSetupFile != null) {
directXDir = dxSetupFile.parent.toFile()
}
}

if (directXDir.exists()) {
val imageFs = ImageFs.find(context)
val rootDir = imageFs.rootDir
val windowsDir = File(rootDir, ImageFs.WINEPREFIX + "/drive_c/windows")

// Do this once at app startup or before the loop
SevenZip.initSevenZipFromPlatformJAR()

directXDir.walkTopDown()
.filter { file ->
val name = file.name.lowercase()
val isAudioType =
name.contains("xaudio") ||
name.contains("xact") ||
name.contains("x3daudio")

isAudioType && file.extension.equals("cab", ignoreCase = true)
}
.forEach { cabFile ->
Timber.tag("replaceXAudioDllsFromRedistributable").d("Processing cabinet: ${cabFile.name}")

if (cabFile.name.lowercase().contains("x86")) {
val targetDir = File(windowsDir, "syswow64")
extractDllsFromCab(cabFile, targetDir)
} else if (cabFile.name.lowercase().contains("x64")) {
val targetDir = File(windowsDir, "system32")
extractDllsFromCab(cabFile, targetDir)
}
}
}
}

private fun extractDllsFromCab(cabFile: File, targetDir: File) {
val raf = RandomAccessFile(cabFile, "r")
val inStream = RandomAccessFileInStream(raf)
val archive: IInArchive = SevenZip.openInArchive(null, inStream)
Comment thread
joshuatam marked this conversation as resolved.

try {
val numberOfItems = archive.numberOfItems
for (i in 0 until numberOfItems) {
val name = archive.getProperty(i, net.sf.sevenzipjbinding.PropID.PATH) as String

if (name.endsWith(".dll", ignoreCase = true)) {
val outFile = File(targetDir, name.lowercase())
if (outFile.exists()) {
Comment thread
joshuatam marked this conversation as resolved.
outFile.delete()
}

FileOutputStream(outFile).use { fos ->
archive.extract(
intArrayOf(i), false,
object : IArchiveExtractCallback {
override fun getStream(index: Int, extractAskMode: ExtractAskMode): ISequentialOutStream? {
if (extractAskMode != ExtractAskMode.EXTRACT) return null

return ISequentialOutStream { data ->
fos.write(data)
data.size // Return the number of bytes written
}
}

override fun prepareOperation(extractAskMode: ExtractAskMode) {}
override fun setOperationResult(extractOperationResult: ExtractOperationResult) {}
override fun setCompleted(completeValue: Long) {}
override fun setTotal(total: Long) {}
}
)
}

Timber.tag("replaceXAudioDllsFromRedistributable").d("Extracted: ${outFile.name}")
}
}
} finally {
archive.close()
inStream.close()
raf.close()
}
}
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ spongycastle = "1.58.0.0" # https://mvnrepository.com/artifact/com.madgag.spongy
timber = "5.0.1" # https://mvnrepository.com/artifact/com.jakewharton.timber/timber
zstd-jni = "1.5.7-5" # https://mvnrepository.com/artifact/com.github.luben/zstd-jni
zxing = "3.5.3" # https://mvnrepository.com/artifact/com.google.zxing/core
sevenzipjbinding = "Release-16.02-2.03" # https://github.com/omicronapps/7-Zip-JBinding-4Android

[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
Expand Down Expand Up @@ -92,6 +93,7 @@ spongycastle = { group = "com.madgag.spongycastle", name = "prov", version.ref =
timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
zstd-jni = { group = "com.github.luben", name = "zstd-jni", version.ref = "zstd-jni" }
zxing = { group = "com.google.zxing", name = "core", version.ref = "zxing" }
sevenzipjbinding = { group = "com.github.omicronapps", name = "7-Zip-JBinding-4Android", version.ref = "sevenzipjbinding" }

# Testing
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
Expand Down
6 changes: 6 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pluginManagement {
}
mavenCentral()
gradlePluginPortal()
maven {
Comment thread
joshuatam marked this conversation as resolved.
setUrl("https://jitpack.io")
}
Comment thread
joshuatam marked this conversation as resolved.
}
}
dependencyResolutionManagement {
Expand All @@ -17,6 +20,9 @@ dependencyResolutionManagement {
google()
mavenCentral()
maven { url = uri("https://central.sonatype.com/repository/maven-snapshots/") } // JavaSteam
maven {
url = uri("https://jitpack.io") // 7Zip
}
}
}

Expand Down
Loading