diff --git a/package.json b/package.json index 23e4980932..d1b98c2c04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "heroic", - "version": "2.5.1", + "version": "2.5.2", "private": true, "main": "build/electron/main.js", "homepage": "./", diff --git a/src/backend/config.ts b/src/backend/config.ts index da64b98e24..2ebb2d731e 100644 --- a/src/backend/config.ts +++ b/src/backend/config.ts @@ -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 } diff --git a/src/backend/tools.ts b/src/backend/tools.ts index 4242d24985..1ab8104009 100644 --- a/src/backend/tools.ts +++ b/src/backend/tools.ts @@ -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' @@ -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' @@ -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`, { @@ -212,35 +217,23 @@ 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 } @@ -248,7 +241,7 @@ export const DXVK = { logInfo([`installing ${tool} on...`, prefix], { prefix: LogPrefix.DXVKInstaller }) - // backup current dlls on the prefix + if (currentVersion === globalVersion) { logInfo(`${tool} already installed!`, { prefix: LogPrefix.DXVKInstaller @@ -256,79 +249,60 @@ export const DXVK = { 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 } @@ -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()) { @@ -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) => { @@ -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`