Skip to content

Commit

Permalink
[Fix] DXVK + Winetricks on macOS (#2166)
Browse files Browse the repository at this point in the history
* fix: dxvk + winetricks on mac

* feat: use mdfind to search for wine versions

* fix: simplify dxvk & pass other paths to winetricks

* chore: remove overrideType

* chore: version
  • Loading branch information
flavioislima authored Dec 14, 2022
1 parent 4af5664 commit d0508f7
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 131 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "heroic",
"version": "2.5.1",
"version": "2.5.2",
"private": true,
"main": "build/electron/main.js",
"homepage": "./",
Expand Down
49 changes: 27 additions & 22 deletions src/backend/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,30 +159,35 @@ abstract class GlobalConfig {
if (!isMac) {
return wineSet
}
const apps = readdirSync(`${userHome}/Applications`)
for (const app of apps) {
if (app.includes('Wine') && app.includes('.app')) {
const wineBin = `${userHome}/Applications/${app}/Contents/Resources/wine/bin/wine64`
if (existsSync(wineBin)) {
try {
const { stdout: out } = await execAsync(`'${wineBin}' --version`)
const version = out.split('\n')[0]
wineSet.add({
...this.getWineExecs(wineBin),
lib: `${userHome}/Applications/Wineskin/${app}/Contents/SharedSupport/wine/lib`,
lib32: `${userHome}/Applications/Wineskin/${app}/Contents/SharedSupport/wine/lib`,
name: `Wine - ${version}`,
type: 'wine',
bin: wineBin
})
} catch (error) {
logError(`Error getting wine version for ${wineBin}`, {
prefix: LogPrefix.GlobalConfig
})
await execAsync('mdfind kMDItemCFBundleIdentifier = "*.wine"').then(
async ({ stdout }) => {
stdout.split('\n').forEach((winePath) => {
const infoFilePath = join(winePath, 'Contents/Info.plist')
if (winePath && existsSync(infoFilePath)) {
const info = plistParse(
readFileSync(infoFilePath, 'utf-8')
) as PlistObject
const version = info['CFBundleShortVersionString'] || ''
const name = info['CFBundleName'] || ''
const wineBin = join(
winePath,
'/Contents/Resources/wine/bin/wine64'
)
if (existsSync(wineBin)) {
wineSet.add({
...this.getWineExecs(wineBin),
lib: `${winePath}/Contents/Resources/wine/lib`,
lib32: `${winePath}/Contents/Resources/wine/lib`,
bin: wineBin,
name: `${name} - ${version}`,
type: 'wine',
...this.getWineExecs(wineBin)
})
}
}
}
})
}
}
)
return wineSet
}

Expand Down
199 changes: 91 additions & 108 deletions src/backend/tools.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { WineInstallation } from 'common/types'
import axios from 'axios'
import {
copyFileSync,
existsSync,
readFileSync,
renameSync,
writeFile,
writeFileSync,
rmSync,
readdirSync
readdirSync,
copyFile,
rm
} from 'graceful-fs'
import { exec, spawn } from 'child_process'

Expand All @@ -23,7 +22,7 @@ import {
} from './constants'
import { logError, logInfo, LogPrefix, logWarning } from './logger/logger'
import i18next from 'i18next'
import { dirname } from 'path'
import { dirname, join } from 'path'
import { isOnline } from './online_monitor'
import { showDialogBoxModalAuto } from './dialog/dialog'
import { validWine } from './launcher'
Expand Down Expand Up @@ -187,7 +186,13 @@ export const DXVK = {
prefix: LogPrefix.DXVKInstaller
})
if (existsSync(currentVersionCheck)) {
rmSync(currentVersionCheck)
rm(currentVersionCheck, { force: true }, (err) => {
if (err) {
logError([`Error removing ${tool} version information`, err], {
prefix: LogPrefix.DXVKInstaller
})
}
})
}

logInfo(`Removing ${tool} files`, {
Expand All @@ -212,123 +217,92 @@ export const DXVK = {
resolve(true)
})

// restore the backup dlls
await new Promise((resolve) => {
dlls.forEach((dll) => {
const dllPath = `${winePrefix}/drive_c/windows/system32/${dll}.bak`
const dllPath64 = `${winePrefix}/drive_c/windows/syswow64/${dll}.bak`
logInfo(`Restoring ${dll}`, { prefix: LogPrefix.DXVKInstaller })
if (existsSync(dllPath)) {
renameSync(dllPath, `${winePrefix}/drive_c/windows/system32/${dll}`)
}
if (!isMac) {
if (existsSync(dllPath64)) {
renameSync(
dllPath64,
`${winePrefix}/drive_c/windows/syswow64/${dll}`
)
}
}
})
resolve(true)
})
// run wineboot -u restore the old dlls
const restoreDlls = `WINEPREFIX='${winePrefix}' '${wineBin}' wineboot -u`
logInfo('Restoring old dlls', { prefix: LogPrefix.DXVKInstaller })
await execAsync(restoreDlls)

// unregister the dlls on the wine prefix
await new Promise((resolve) => {
dlls.forEach(async (dll) => {
const unregisterDll = `WINEPREFIX='${winePrefix}' '${wineBin}' reg delete 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v ${dll} /f`
logInfo(`Unregistering ${dll}`, { prefix: LogPrefix.DXVKInstaller })
await execAsync(unregisterDll)
dlls.forEach(async (dll) => {
dll = dll.replace('.dll', '')
const unregisterDll = `WINEPREFIX='${winePrefix}' '${wineBin}' reg delete 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v ${dll} /f`
logInfo(`Unregistering ${dll}`, { prefix: LogPrefix.DXVKInstaller })
exec(unregisterDll, (error) => {
if (error) {
logError([`Error when unregistering ${dll}`, error], {
prefix: LogPrefix.DXVKInstaller
})
}
})
resolve(true)
})
return true
}

logInfo([`installing ${tool} on...`, prefix], {
prefix: LogPrefix.DXVKInstaller
})
// backup current dlls on the prefix

if (currentVersion === globalVersion) {
logInfo(`${tool} already installed!`, {
prefix: LogPrefix.DXVKInstaller
})
return true
}

logInfo(`Backing up current ${tool} dlls`, {
prefix: LogPrefix.DXVKInstaller
})

await new Promise((resolve) => {
dlls.forEach(async (dll) => {
const dllPath = `${winePrefix}/drive_c/windows/system32/${dll}`
if (existsSync(dllPath)) {
renameSync(
`${winePrefix}/drive_c/windows/system32/${dll}`,
`${winePrefix}/drive_c/windows/system32/${dll}.bak`
)
}

// macOs supports 64 bits only
if (!isMac) {
const dllPath64 = `${winePrefix}/drive_c/windows/syswow64/${dll}`
if (existsSync(dllPath64)) {
renameSync(
`${winePrefix}/drive_c/windows/syswow64/${dll}`,
`${winePrefix}/drive_c/windows/syswow64/${dll}.bak`
)
// copy the new dlls to the prefix
dlls.forEach((dll) => {
if (!isMac) {
copyFile(
`${toolPathx32}/${dll}`,
`${winePrefix}/drive_c/windows/syswow64/${dll}`,
(err) => {
if (err) {
logError([`Error when copying ${dll}`, err], {
prefix: LogPrefix.DXVKInstaller
})
}
}
}
})
resolve(true)
})
)
}

// copy the new dlls to the prefix
await new Promise((resolve) => {
dlls.forEach((dll) => {
if (!isMac) {
copyFileSync(
`${toolPathx32}/${dll}`,
`${winePrefix}/drive_c/windows/syswow64/${dll}`
)
copyFile(
`${toolPathx64}/${dll}`,
`${winePrefix}/drive_c/windows/system32/${dll}`,
(err) => {
if (err) {
logError([`Error when copying ${dll}`, err], {
prefix: LogPrefix.DXVKInstaller
})
}
}

copyFileSync(
`${toolPathx64}/${dll}`,
`${winePrefix}/drive_c/windows/system32/${dll}`
)
})
resolve(true)
)
})

// register dlls on the wine prefix
await new Promise((resolve) => {
dlls.forEach(async (dll) => {
await execAsync(
`WINEPREFIX='${winePrefix}' '${wineBin}' reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v ${dll} /d native /f `,
execOptions
)
.then(() => {
logInfo(`${dll} registered!`, {
dlls.forEach(async (dll) => {
// remove the .dll extension otherwise will fail
dll = dll.replace('.dll', '')
exec(
`WINEPREFIX='${winePrefix}' '${wineBin}' reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v ${dll} /d native,builtin /f `,
execOptions,
(err) => {
if (err) {
logError([`Error when registering ${dll}`, err], {
prefix: LogPrefix.DXVKInstaller
})
})
.catch((error) => {
logError([`Error when registering ${dll}`, error], {
prefix: LogPrefix.DXVKInstaller
})
})
})
exec(`echo ${globalVersion} > ${currentVersionCheck}`)
writeFile(currentVersionCheck, globalVersion, (err) => {
if (err) {
logError([`Error when writing ${tool} version`, err], {
prefix: LogPrefix.DXVKInstaller
})
}
}
})
resolve(true)
)
})

exec(`echo ${globalVersion} > ${currentVersionCheck}`)

writeFile(currentVersionCheck, globalVersion, (err) => {
if (err) {
logError([`Error when writing ${tool} version`, err], {
prefix: LogPrefix.DXVKInstaller
})
}
})
return true
}
Expand All @@ -340,8 +314,11 @@ export const Winetricks = {
return
}

const url =
const linuxUrl =
'https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks'
const macUrl =
'https://raw.githubusercontent.com/The-Wineskin-Project/winetricks/macOS/src/winetricks'
const url = isMac ? macUrl : linuxUrl
const path = `${heroicToolsPath}/winetricks`

if (!isOnline()) {
Expand Down Expand Up @@ -379,12 +356,25 @@ export const Winetricks = {

const winepath = dirname(wineBin)

const envs = {
const linuxEnvs = {
...process.env,
WINEPREFIX: winePrefix,
PATH: `${winepath}:${process.env.PATH}`
}

const wineServer = join(winepath, 'wineserver')

const macEnvs = {
...process.env,
WINEPREFIX: winePrefix,
WINESERVER: wineServer,
WINE: wineBin,
WINE64: wineBin,
PATH: `/opt/homebrew/bin:${process.env.PATH}`
}

const envs = isMac ? macEnvs : linuxEnvs

const executeMessages = [] as string[]
let progressUpdated = false
const appendMessage = (message: string) => {
Expand All @@ -404,17 +394,10 @@ export const Winetricks = {
}, 1000)

// check if winetricks dependencies are installed
const dependencies = [
'7z',
'cabextract',
'zenity',
'unzip',
'curl',
'wine'
]
const dependencies = ['7z', 'cabextract', 'zenity', 'unzip', 'curl']
dependencies.forEach(async (dependency) => {
try {
await execAsync(`which ${dependency}`, execOptions)
await execAsync(`which ${dependency}`, { ...execOptions, env: envs })
} catch (error) {
appendMessage(
`${dependency} not installed! Winetricks might fail to install some packages or even open`
Expand Down

0 comments on commit d0508f7

Please sign in to comment.