diff --git a/src/backend/__tests__/utils.test.ts b/src/backend/__tests__/utils.test.ts index 93059febc6..c86c2cdc6e 100644 --- a/src/backend/__tests__/utils.test.ts +++ b/src/backend/__tests__/utils.test.ts @@ -197,6 +197,15 @@ describe('backend/utils.ts', () => { expect(getExecutableAndArgs(input)).toEqual(expected) }) + it('should correctly parse executable with .exe extension and no arguments', () => { + const input = 'path/to/application.exe' + const expected = { + executable: 'path/to/application.exe', + launchArgs: '' + } + expect(getExecutableAndArgs(input)).toEqual(expected) + }) + it('should correctly parse executable with .app extension and no arguments', () => { const input = 'path/to/application.app' const expected = { @@ -226,10 +235,10 @@ describe('backend/utils.ts', () => { }) it('should return empty strings if no executable is found', () => { - const input = '--arg1 --arg2' + const input = '' const expected = { executable: '', - launchArgs: '--arg1 --arg2' + launchArgs: '' } expect(getExecutableAndArgs(input)).toEqual(expected) }) @@ -260,5 +269,41 @@ describe('backend/utils.ts', () => { } expect(getExecutableAndArgs(input)).toEqual(expected) }) + + it('should handle simple executable name without args', () => { + const input = 'executable.exe' + const expected = { + executable: 'executable.exe', + launchArgs: '' + } + expect(getExecutableAndArgs(input)).toEqual(expected) + }) + + it('should handle simple executable name with args', () => { + const input = 'steam --no-browser' + const expected = { + executable: 'steam', + launchArgs: '--no-browser' + } + expect(getExecutableAndArgs(input)).toEqual(expected) + }) + + it('should handle absolute path from /usr/bin', () => { + const input = '/usr/bin/steam --no-browser' + const expected = { + executable: '/usr/bin/steam', + launchArgs: '--no-browser' + } + expect(getExecutableAndArgs(input)).toEqual(expected) + }) + + it('should handle absolute path from /usr/local/bin', () => { + const input = '/usr/local/bin/custom-launcher --fullscreen' + const expected = { + executable: '/usr/local/bin/custom-launcher', + launchArgs: '--fullscreen' + } + expect(getExecutableAndArgs(input)).toEqual(expected) + }) }) }) diff --git a/src/backend/storeManagers/storeManagerCommon/games.ts b/src/backend/storeManagers/storeManagerCommon/games.ts index fde4d1705c..f36131eee0 100644 --- a/src/backend/storeManagers/storeManagerCommon/games.ts +++ b/src/backend/storeManagers/storeManagerCommon/games.ts @@ -358,7 +358,13 @@ export async function launchGame( ) // On Mac, it gives an error when changing the permissions of the file inside the app bundle. But we need it for other executables like scripts. if (isLinux || (isMac && !exeOnly.endsWith('.app'))) { - await chmod(exeOnly, 0o775) + try { + await chmod(exeOnly, 0o775) + } catch (error) { + logWarning( + 'Was not possible to change permission to this file, maybe the owner is Root?' + ) + } } } diff --git a/src/backend/utils.ts b/src/backend/utils.ts index 8e7eac742f..23836ff63e 100644 --- a/src/backend/utils.ts +++ b/src/backend/utils.ts @@ -54,7 +54,7 @@ import { LogPrefix, logWarning } from './logger/logger' -import { basename, dirname, join, normalize } from 'path' +import { basename, dirname, isAbsolute, join, normalize } from 'path' import { runRunnerCommand as runLegendaryCommand } from 'backend/storeManagers/legendary/library' import { gameInfoStore, @@ -1242,20 +1242,48 @@ export function getPlatformName(platform: string): PlatformName { return platformMap[platform] || platform || 'Unknown' } +const splitExeAndArgs = (executableWithArgs: string) => { + const executable = executableWithArgs.split(' -')[0] + const launchArgs = executableWithArgs.replace(executable, '').trim() + + return [executable, launchArgs] +} + export function getExecutableAndArgs(executableWithArgs: string): { executable: string launchArgs: string } { + if (!executableWithArgs) { + return { executable: '', launchArgs: '' } + } + + // Handle absolute paths first + const isAbsolutePath = isAbsolute(executableWithArgs) + if (isAbsolutePath) { + const [exe, args] = splitExeAndArgs(executableWithArgs) + return { executable: exe, launchArgs: args } + } + + // Handle .app paths if (executableWithArgs.includes('.app')) { - const executable = executableWithArgs.split(' -')[0] + const [executable, launchArgs] = splitExeAndArgs(executableWithArgs) + return { executable, launchArgs } + } + + // Handle common extensions + const matchWithExt = executableWithArgs.match(/^(.*?\.(exe|bin|sh))/i) + if (matchWithExt) { + const executable = matchWithExt[0] const launchArgs = executableWithArgs.replace(executable, '').trim() return { executable, launchArgs } } - const match = executableWithArgs.match(/^(.*?\.(exe|bin|sh))/i) - const executable = match ? match[0] : '' - const launchArgs = executableWithArgs.replace(executable, '').trim() - return { executable, launchArgs } + // Handle executables without extension + const [executable, ...argParts] = splitExeAndArgs(executableWithArgs) + return { + executable, + launchArgs: argParts.join(' ') + } } function roundToTenth(x: number) {