class Object
Constants
- V_DBG
- V_INFO
Public Instance Methods
Purpose: Print the help output
# File wwine, line 776 def Help () puts "wwine "+$version puts "" puts "Usage: wwine (WWINE PARAMETERS) PROGRAM -- [PROGRAM ARGUMENTS]" puts "" printHelp('-h','--help','Display this help text') printHelp('-w','--wine','Select the wine to use. Any wine listed in "wwine --list" or the path to a wine binary or crossover installation directory.') printHelp('-b','--bottle','Use the selected bottle (~/.wwinebottles/[NAME] for wine, CX bottle or cedega folder, depending on the --wine in use). The bottle will be created if it does not exist.') printHelp('','--list-proton-bottles','Output a list of all available Proton/Steam game bottles') printHelp('','--debug','Enable wine debugging output with the supplied levels (using the format of the WINEDEBUG environment variable)') printHelp('','--env','Set the WINE and WINEPREFIX environment variables to match the parameters provided to wwine and optionally run a program with those variables set.') printHelp('','--arch','Set the wine architecture. Accepts win32 and win64. The default is win32 if --wine does not end with "64"') printHelp('-l','--list','List all available wine versions') printHelp('-k','--kill','Attempt to kill all running wine processes') printHelp('','--drykill','Print what --kill would have done, but don\'t actually do anything') printHelp('','--gamemode','Enable Feral GameMode') printHelp('','--no-gamemode','Disable Feral GameMode') printHelp('','--wrap','Write a wrapper script of your current wwine command to the path supplied') printHelp('','--wrapdir','Use the supplied directory as the working directory for the script created by --wrap (default: current directory)') printHelp('-s','--from','Load parameters and program from the wrapper script supplied. Command-line arguments overrides settings from wrapper script.') printHelp('-v','--verbose','Enable verbose mode. Supply multiple times to further increase verbosity.') printHelp('','--man','Show the wwine manpage') end
Purpose: Print debugging info
# File wwine, line 953 def debugInfo () outFormat = '%-30s: %s'+"\n" puts "wwine "+$version # Load a --from file if it is set if $wwineDataFrom loadWwineDataFromFile($wwineDataFrom,true) end # Fetch the md5sum of us. This will at least give a certain clue about the revision # of wwine used. Primarily useful for identifying older git clones in bug reports. begin if inPath('md5sum') outStr = nil out = IO.popen('md5sum '+File.expand_path($0)) outStr = out.readline outStr.sub!(/\s+.*$/,'') outStr.chomp! printf(outFormat,'wwine md5sum',outStr) else printf(outFormat,'wwine md5sum','(md5sum command missing)') end rescue printf(outFormat,'wwine md5sum','(exception: '+$!+')'); end listWines(true) exit(0) end
Purpose: Emulate –version for nonstandard wine
# File wwine, line 486 def emulateVersionOutput(wine) # For CX* we can actually output a useful version number, namely the # wine version that CX* release was based on if wine == 'cxgames' || wine == 'cxoffice' || wine == 'crossover' cxgBin = getCXwithParams(wine,nil,false) cxVer, cxwVer = getCXVersionsFrom( cxgBin ) # CX* wine --versions often contain git revisions and other additional # information in the version number, this strips that out. cxwVer.sub!(/-.*/,'') puts 'wine-'+cxwVer # For proton we use the proton version number elsif wine =~ /^proton-/ version = wine.sub(/^proton-(\d+.\d+)\D*/,'\1') puts 'wine-'+version # For cedega we just output an invalid version number in the same syntax # as vanilla wine would have done elsif resolveWine(wine) == 'gametree' puts 'wine-0.0.0' end end
Purpose: Set an environment variable
# File wwine, line 766 def envPut (name,value) if value == nil puts "ERROR: Tried to set nil env var: '"+name+"'" exit 1 end vputs(V_DBG,'Set '+name+'='+value) ENV[name] = value; end
Purpose: Prepare to generate a wrapper script
# File wwine, line 451 def generateWrapper (wine,bottle,targetFile,wineCommand,args,type = nil) if File.exists?(targetFile) puts(targetFile+': already exists. Bailing out') puts('Remove the target file first, then try again') exit(1) end wwineCmd, wwineInfo = generateWwineCmd(wine,bottle,wineCommand,type) # Append -- if it isn't already if ! args.join(' ').match(/ -- /) wwineCmd.push('--') end wwineCmd.concat(args) if $wrapperCwd == nil $wrapperCwd = Dir.pwd end wwineInfo['Dir'] = $wrapperCwd wineprefix = nil # For wine there is no --bottle definition in the wine command itself, # so we need to export a wineprefix in the launch script if (type == 'wine' || type =~ /^(pol|lutris)[-:]/) && ENV['WINEPREFIX'] wineprefix = ENV['WINEPREFIX'].dup end writeWrapper(targetFile ,wwineCmd, wineCommand, $wrapperCwd, wwineInfo, args, wineprefix) end
Purpose: Generate a wwine command and wwineInfo array
# File wwine, line 414 def generateWwineCmd (wine,bottle,wineCommand,type = nil) wwineCmd = [] wwineInfo = { 'Dir' => 'nil', 'Bottle' => 'nil', 'Wine' => 'nil', 'GameMode' => 'nil', } # Generate a wwine command-line if $verbosity > 0 wwineCmd.push('--verbose') end if $wineDebug wwineCmd.push('--debug',$wineDebug) wwineInfo['Debug'] = $wineDebug end if $wineArch wwineCmd.push('--arch',$wineArch) wwineInfo['Arch'] = $wineArch end if $preloadGameMode wwineCmd.push('--gamemode') wwineInfo['GameMode'] = 'true' end if wine wwineCmd.push('--wine',wine) wwineInfo['Wine'] = wine end if bottle wwineCmd.push('--bottle',bottle) wwineInfo['Bottle'] = bottle; end return wwineCmd, wwineInfo end
Purpose: Detect which wine to use and get parameters for that one Returns: type,cmd
# File wwine, line 1574 def getAutoWithParams (bottle, missingIsFatal = true, returnFlavour = false) cmd = getWineWithParams('wine',bottle,false) type = 'wine' if cmd == nil cmd = getCXwithParams('cxgames',bottle,false) type = 'cxgames' end if cmd == nil cmd = getCXwithParams('cxoffice',bottle,false) type = 'cxoffice' end if cmd == nil cmd = getGameTreeWithParams('gametree',bottle,false) type = 'gametree' end if cmd == nil if missingIsFatal puts 'Failed to detect any wine version at all.' exit(1) else return nil end else vputs(V_DBG,'Autodetected wine command: '+cmd.join(' ')) return type,cmd end end
Purpose: Get cx* version
# File wwine, line 909 def getCXVersionsFrom (source,wantString = false) cxVer = nil cxwVer = nil if source != nil begin # Store the binfile for use later binFile = source[0] # pop off the dummy command source.pop # Add version source.push('--version') source = source.join(' ') out = IO.popen(source) # The info we need is on line two cxVer = out.readline cxVer = out.readline # Get the actual version number by stripping away any leading nondigit text cxVer.sub!(/^\D+/,'') cxVer.chomp! cxwVer = getWineVersion(binFile.sub(/cxstart$/,'wineserver'),true,'-v') rescue cxwVer = nil cxVer = nil end end if wantString # If a string has been requested, generate one and return it if cxVer != nil if cxwVer != nil return cxVer+' (based on wine '+cxwVer+')' else return cxVer end else return nil end else return cxVer,cxwVer end end
Purpose: Get parameters for crossover
# File wwine, line 1195 def getCXwithParams (wine,bottle, missingIsFatal = true, getBottleDir = false, getCXpath = false) final = [] cxdir = nil pathMode = nil if File.directory?(wine) if Dir.glob(wine+'/etc/cxgames*.conf').length > 0 installName = 'cxgames' mode = 'cxgames' else installName = 'cxoffice' mode = 'crossover' end pathMode = wine wines = [ wine ] else installName = wine if wine == 'crossover' installName = 'cxoffice' end mode = wine # Various crossover install paths wines = ['/opt/'+installName, ENV['HOME']+'/'+installName, ENV['HOME']+'/.local/'+installName, ENV['HOME']+'/games/'+installName] end bottleDir = '.'+installName wines.each do |path| if File.exists?(path) if ( File.executable?(path+'/bin/cximportbottles') || File.executable?(path+'/bin/crossover') ) and installName != 'cxgames' # Perform additional detection of dotDir, as that one tends to change dotDir = Dir.glob(path+'/etc/cxoffice*.conf').shift if( dotDir != nil && dotDir.sub!(/^.*(cxoffice\d*)\.conf$/,'\1') ) mode = 'crossover' cxdir = path bottleDir = '.'+dotDir break end elsif File.executable?(path+'/bin/cxstart') cxdir = path break end end end # If we're using $cx*Path, perform some additional detection if pathMode != nil and mode != 'crossover' if File.exists?(pathMode+'/etc/'+installName+'.conf') cxdir = pathMode else cxdir = nil end end # If the dir does not exist, give up if not cxdir if !missingIsFatal vputs(V_DBG,'Failed to detect cxdir for '+mode) return nil end if pathMode != nil puts('Could not find a directory named "'+installName+'" in '+pathMode) puts('and '+pathMode+' does not appear to be a '+installName+' install directory') if installName == 'cxgames' reverse = 'cxoffice' else reverse = 'cxgames' end else puts('Failed to locate '+wine+ ' (cx)') puts('You could try to explicitly tell wwine where '+wine+' is installed') puts('by supplying the full path to --wine') end exit(1) elsif getCXpath return cxdir end if bottle =~ /\// puts wine+' (cx) does not support bottles that exist outside of ~/'+bottleDir exit 1 end final.push(cxdir+'/bin/cxstart') if(bottle != nil && bottle.length > 0) final.push('--bottle',bottle) end if $wineDebug final.push('--debugmsg',$wineDebug) end final.push('--'); # Create the bottle if it does not exist if bottle != nil && !File.exists?(ENV['HOME']+'/'+bottleDir+'/'+bottle) vputs(V_DBG,ENV['HOME']+'/'+bottleDir+'/'+bottle+' did not exist') puts 'The bottle '+bottle+' did not exist, creating...' runcmd([ cxdir+'/bin/cxbottle', '--bottle',bottle,'--create','--template','winxp'],'system') if ! File.exists?(ENV['HOME']+'/'+bottleDir+'/'+bottle) puts 'Bottle creation failed.' exit 1 end end if getBottleDir return ENV['HOME']+'/'+bottleDir+'/'+bottle else vputs(V_DBG,'Detected cx: '+final.join(' ')) return final end end
Purpose: Get parameters for PlayOnLinux or Lutris
# File wwine, line 1343 def getCustomwithParams(wine,bottle, type = 'POL', missingIsFatal = true, returnDetected = false) winePaths = [] polMode = true if type =~ /^pol/i winePaths = [ ENV['HOME']+'/.PlayOnLinux/wine/linux-x86', ENV['HOME']+'/.PlayOnLinux/wine/linux-amd64' ] else polMode = false winePaths = [ ENV['HOME']+'/.local/share/lutris/runners/wine/' ] end detectedWine = nil detectedWineDir = nil winePaths.each do |path| if File.exists?(path+'/'+wine+'/bin/wine') detectedWine = path+'/'+wine+'/bin/wine' detectedWineDir = path+'/'+wine end end if returnDetected return detectedWine end if not detectedWine if !missingIsFatal vputs(V_DBG,'Failed to locate wine '+wine) return nil end puts('There does not appear to be a wine named "'+wine+'"') puts('installed via PlayOnLinux. See "wwine --list" for a list of available wine versions.') exit(1) end envPut('PATH',detectedWineDir+'/bin/:'+ENV['PATH']) envPut('WINESERVER',detectedWineDir+'/bin/wineserver') envPut('WINELOADER',detectedWine) envPut('WINEDLLPATH',detectedWineDir+'/lib/wine:'+ENV['WINEDLLPATH'].to_s) envPut('LD_LIBRARY_PATH',detectedWineDir+'/lib:'+ENV['LD_LIBRARY_PATH'].to_s) return getWineWithParams(detectedWine,bottle,missingIsFatal,polMode) end
Purpose: Get information from /proc/PID/environ
# File wwine, line 189 def getFromEnviron(path) info = Hash.new info['bottle'] = '(?)' info['wine'] = '(?)' if File.readable?(path) source = File.open(path) env = Hash.new source.each("\0") do |entry| key = entry.sub(/^([^=]+)=.*$/,'\1').chomp value = entry.sub(/^[^=]+=(.*)$/,'\1').sub(/\0/,'').chomp env[key] = value end if env['__WWINE_INT_XWINE'] info['wine'] = env['__WWINE_INT_XWINE'] elsif env['CX_ROOT'] && env['CX_ROOT'].match(/cxoffice/) info['wine'] = 'crossover' elsif env['CX_ROOT'] && env['CX_ROOT'].match(/cxgames/) info['wine'] = 'cxgames' elsif env['WINE'] info['wine'] = env['WINE'] elsif env['WINELOADER'] info['wine'] = 'wine' elsif info['bottle'] == '.wine-pipelight' info['wine'] = 'pipelight' info['bottle'] = '(pipelight)' elsif env['WINEDLLPATH'] =~ /Proton/ proton = env['WINEDLLPATH'].sub(/.*Proton\s+([^\/]+).*/,'proton-\1') if proton !~ /\// info['wine'] = proton.downcase.sub(/\s/,'') end end if env['CX_BOTTLE'] info['bottle'] = env['CX_BOTTLE'] elsif info['wine'] =~ /proton/i bottle = env['__WWINE_INT_XBOTTLE'] || env['WINEPREFIX'] || env['STEAM_COMPAT_DATA_PATH'] if bottle if File.basename(bottle) == 'pfx' bottle = File.realpath(bottle+'/../') end bottleId = File.basename(bottle) if bottleId =~ /\D/ info['bottle'] = '(?)' else info['bottle'] = 'steam:'+bottleId end end elsif env['__WWINE_INT_XBOTTLE'] info['bottle'] = env['__WWINE_INT_XBOTTLE'] elsif env['WINEPREFIX'] info['bottle'] = File.basename(env['WINEPREFIX']) if info['bottle'] == 'pfx' && info['wine'] =~ /proton/i info['bottle'] = File.basename(File.dirname(ENV['WINEPREFIX'])) end else info['bottle'] = '(default)' end end return info end
Purpose: Get cedega version string
# File wwine, line 861 def getGameTreeVersionString () cedegaVer = nil if inPath('cedega') || inPath('gametree') begin # We need the HOME variable if ENV.has_key?('HOME') # The cedega RC file rcFile = ENV['HOME']+'/.cedega/.cedegarc' # The cedega build number file uiBuildFile = ENV['HOME']+'/.cedega/.ui/BUILDNUMBER' # If we have the rc file, open it and fetch the current default # engine version from the 'default' setting. It is the closest we are # going to get to a current version number without jumping through a lot # of hoops. if File.exists?(rcFile) ind = File.open(rcFile) ind.each do |line| if line.match(/^default=/) line.sub!(/^default=/,'') line.chomp! cedegaVer = line break end end end # If we have a default ver, fetch the UI version as well, as we have to pass # through the UI, that number can be useful if cedegaVer != nil && File.exists?(uiBuildFile) ind = File.open(uiBuildFile) buildNo = ind.gets(nil) buildNo.chomp! if buildNo != nil cedegaVer.concat(' (UI build '+buildNo+')'); end end end rescue end if cedegaVer == nil cedegaVer = 'present' end end return cedegaVer end
Purpose: Get parameters for gametree
# File wwine, line 1307 def getGameTreeWithParams (wine,bottle, missingIsFatal = true) final = [] gametreeBin = nil puts("WARNING: gametree/cedega support will be deprecated in wwine 0.8") if inPath('gametree') gametreeBin = 'gametree' elsif inPath('gametree') gametreeBin = 'cedega' else if missingIsFatal puts('gametree does not appear to be installed') exit(1) else return nil end end final.push(gametreeBin,'--install') if $wineDebug finalpush('--debugmsg',$wineDebug) end # Use wwineFolder as the folder if no bottle was supplied if (bottle == nil || bottle.length == 0) bottle = 'wwineFolder' end final.push(bottle) vputs(V_DBG,'Detected gametree: '+final.join(' ')) return final end
Purpose: Get a list of Proton base directory locations
# File wwine, line 1477 def getProtonBaseDirs () dirs = [ ENV['HOME']+'/.steam/root/SteamApps/', ENV['HOME']+'/.steam/root/steamapps/' ] steamConfigFile = ENV['HOME']+'/.steam/root/config/config.vdf' if File.exists?(steamConfigFile) source = File.open(steamConfigFile) source.each("\n") do |entry| if entry =~ /\s*\"BaseInstallFolder_\d+\"\s*/ dir = entry.sub(/^\s*\"BaseInstallFolder_\d+\"\s*\"(.*)\"/,'\1').chomp dirs.push(dir+'/steamapps/') end end end return dirs end
Purpose: Get the name of the game that owns a Proton bottle (converts ID to name)
# File wwine, line 1494 def getProtonBottleName (id) dirs = getProtonBaseDirs() dirs.each do |entry| manifestFile = entry+'/appmanifest_'+id+'.acf'; if File.exists?(manifestFile) manifest = File.open(manifestFile) manifest.each("\n") do |entry| if entry =~ /\s*\"(name|installdir)\"\s*/ return entry.sub(/^\s*\"(name|installdir)\"\s*\"(.*)\"/,'\2').chomp end end end end return nil end
Purpose: Get a list of Proton bottles Returns a hash, mapping the key (ID) to another hash. The inner hash kontains the keys “directory” (path to the bottle) and “name” (name of the game owning the bottle)
# File wwine, line 1514 def getProtonBottles () dirs = getProtonBaseDirs() bottles = {} dirs.each do |entry| Dir.glob(entry+'/compatdata/*') do |bottle| if Dir.exists?(bottle+'/pfx') bottle = File.realpath(bottle) id = File.basename(bottle) name = getProtonBottleName(id) if name == nil name = 'uninstalled game ('+bottle+')' end bottles[id] = { "name" => name, "directory" => bottle+'/pfx' } end end end return bottles end
Purpose: Get parameters for Proton if multiReturn is true it will return cmd,bottle
# File wwine, line 1385 def getProtonWithParams(wine,bottle, missingIsFatal = true, multiReturn = false) command = [] protons = getProtons() wine = wine.downcase if !protons[wine] if missingIsFatal puts("Could not find a proton version matching \""+wine+"\"") exit(1) else return nil end end if bottle == nil bottle = 'wwine-proton-default-bottle' end runtimeWrapper = ENV['HOME']+'/.steam/root/ubuntu12_32/steam-runtime/run.sh' if ENV.has_key?('STEAM_RUNTIME') && ENV['STEAM_RUNTIME'] == "0" vputs(V_DBG,"Skipping Steam runtime for proton because of the STEAM_RUNTIME env var") elsif !File.exists?(runtimeWrapper) vputs(V_DBG, "Did not find Steam runtime wrapper script, skipping steam runtime") else command.push(runtimeWrapper) end command.push( protons[wine]+'/proton','run') if File.exists?(bottle) && File.basename(bottle) == 'pfx' bottle = File.realpath(bottle+'/../') end bottleRoot = ENV['HOME']+'/.wwinebottles/' if bottle =~ /^\// && File.exists?(bottle+'/pfx') # Proton uses STEAM_COMPAT_DATA_PATH to point to the bottle location envPut('STEAM_COMPAT_DATA_PATH',bottle) else if !Dir.exists?(bottleRoot) vputs(V_INFO,bottleRoot+': does not exist, creating') begin Dir.mkdir(bottleRoot) rescue SystemCallError puts('Failed to create directory "'+bottleRoot+": "+$!.message) puts('Unable to continue.') exit(1) end end if !Dir.exists?(bottleRoot+bottle) vputs(V_INFO,bottleRoot+bottle+': does not exist, creating') begin Dir.mkdir(bottleRoot+bottle) rescue SystemCallError puts('Failed to create directory "'+bottleRoot+": "+$!.message) puts('Unable to continue.') exit(1) end end # Proton always uses /pfx, we make that a symlink to the base bottle if !File.exists?(bottleRoot+bottle+'/pfx') begin FileUtils.ln_s(bottleRoot+bottle,bottleRoot+bottle+'/pfx') rescue SystemCallError puts('Failed to create pfx symlink in '+bottleRoot+bottle+': '+$!.message) puts('Unable to continue') exit(1) end end # Proton uses STEAM_COMPAT_DATA_PATH to point to the bottle location envPut('STEAM_COMPAT_DATA_PATH',bottleRoot+bottle) end # Disable wineArch support. Proton uses its own. if $wineArch puts "Warning: proton does not support --arch, ignoring." $wineArch = nil end # Enable debugging if requested if $wineDebug puts "Notice: proton does not support --debug, ignoring." end vputs(V_DBG,'Detected proton: '+command.join(' ')) if multiReturn return command,bottle+'/pfx' else return command end end
Purpose: Get a list of installed proton versions
# File wwine, line 1553 def getProtons () dirs = getProtonBaseDirs() # Proton gets installed in the same "steam library" directory that the first game # that used said version of Proton is installed to. Thus we need to parse out where # all of Steam's library directories are to be able to produce a complete list. protons = {} # Note: In the case of several identical proton versions we'll just use the last one # we find dirs.each do |entry| Dir.glob(entry+'/common/Proton*') do |instance| if File.executable?(instance+'/proton') version = instance.sub(/.*Proton\s+(\d+\.\d+)\s*(.*)?/,'\1\2').sub(/\s/,'').downcase protons['proton-'+version] = instance end end end return protons end
Purpose: Get wine version
# File wwine, line 835 def getWineVersion (source = 'wine', fromStderr = false, cmdOpt = '--version') wineVer = nil # Don't try to get it if we have no wine to run if inPath(source) || File.executable?(source) begin cmd = [ source,cmdOpt ] Open3.popen3(*cmd) { |stdin, stdout, stderr| # Fetch the line from either stderr or out if fromStderr wineVer = stderr.gets(nil) else wineVer = stdout.gets(nil) end } # Get the actual version number by stripping away any leading nondigit text wineVer.sub!(/^\D+/,'') wineVer.chomp! rescue wineVer = nil end end return wineVer end
Purpose: Get parameters for wine
# File wwine, line 1094 def getWineWithParams (wine,bottle, missingIsFatal = true, playOnLinux = false, proton = false) final = [] possibleWines = [] if wine =~ /^wine(64|32|-unstable(32|64)?)$/ possibleWines = [ wine ] else if $wineArch && $wineArch == 'win64' possibleWines = [ 'wine64','wine-unstable64','wine','wine.bin','wine.real','wine-unstable' ] else possibleWines = [ 'wine32','wine','wine64','wine.bin','wine.real','wine-unstable','wine32-unstable' ] end end # Manual search path, in the odd case none of the above are in $PATH. # Ie. /usr/lib32/wine/wine.bin is the real wine binary on Debian searchPath = [ '/usr/lib32/wine/','/usr/bin','/usr/lib/i386-linux-gnu/wine-unstable','/usr/lib/i386-linux-gnu/wine', ] # If wine is not a path or does not exist, check for wine in the path if !File.exists?(wine) wine = nil possibleWines.each do |w| if inPath(w) wine = w break end end if wine == nil searchPath.each do |p| possibleWines.each do |w| if File.executable?(p+'/'+w) && File.file?(p+'/'+w) wine = p+'/'+w break end end if wine != nil break end end end if wine == nil if missingIsFatal == true puts('"wine" does not appear to be installed') exit(1) else return nil end end end final.push(wine) # wwine's bottle storage wwine_bottleRoot = ENV['HOME']+'/.wwinebottles/' # PlayOnLinux's bottle storage playOnLinux_bottleRoot = ENV['HOME']+'/.PlayOnLinux/wineprefix/' # wwine is the default bottleRoot = wwine_bottleRoot # If a POL bottle exists and we are using a POL wine, then use the # POL bottle, otherwise use the wwine bottle if playOnLinux && bottle != nil if File.exists?(playOnLinux_bottleRoot+bottle) bottleRoot = playOnLinux_bottleRoot vputs(V_INFO,'Using the PlayOnLinux bottle path since a POL bottle named "'+bottle+'" exists') else vputs(V_DBG,'Using the wwine bottle location for POL since no POL bottle named "'+bottle+'" exists') end end # Set WINEPREFIX for bottle support if(bottle != nil && bottle.length > 0) if ENV.has_key?('WINEPREFIX') && ENV['WINEPREFIX'].length > 0 warn 'WINEPREFIX= was already set, overriding it.' end if ! (bottle =~ /^\.?\//) bottle = bottleRoot+bottle if !File.exists?(bottleRoot) vputs(V_INFO,bottleRoot+': does not exist, creating') begin Dir.mkdir(bottleRoot) rescue SystemCallError puts('Failed to create directory "'+bottleRoot+": "+$!) puts('Unable to continue.') exit(1) end end end envPut('WINEPREFIX',bottle) end # Enable debugging if requested if $wineDebug envPut('WINEDEBUG',$wineDebug) # Default to no debugging output if WINEDEBUG is not set yet elsif ! ENV.has_key?('WINEDEBUG') envPut('WINEDEBUG','-all') end vputs(V_DBG,'Detected wine: '+final.join(' ')) return final end
Purpose: Handle an exception
# File wwine, line 1079 def handleException(ex) puts('---') puts('Exception: '+ex.to_s) puts('Backtrace: '+"\n"+ex.backtrace.join("\n")) puts('---') puts() puts('An exception has occurred and wwine can not continue.') puts('This almost certainly reflects a bug in wwine.') puts('Please check that you have the latest version of wwine,') puts('and if you do, report this issue along with the text between the "---" above'); puts('to http://random.zerodogg.org/wwine/bugs') exit(1) end
Purpose: Check for a file in path
# File wwine, line 740 def inPath(exec) ENV['PATH'].split(/:/).each do |part| if File.executable?(part+'/'+exec) and not File.directory?(part+'/'+exec) return true end end return false end
Purpose: Attempt to kill all running wine processes
# File wwine, line 55 def killWine (signal) dryRun = false if signal == 9999 dryRun = true signal = 15 end # Open a pipe to ps to read processes wines = IO.popen('ps uxw') # The result of our ps parsing wineResult = [] # The length of the "wine" name, used to format the output well wineLength = 10 # The name of the signal being sent sigName = 'SIGTERM' # Get an integer version of signal parameter begin signal = Integer(signal) rescue signal = nil end # If the signal parameter is invalid, error out if signal == nil signal = 15 elsif signal > 15 || signal < 0 puts('Argument to --kill must be an integer between 0-15 (or none at all)') exit(1) end # Map of number => name sigNames = { 9 => 'SIGKILL', 15 => 'SIGTERM', 2 => 'SIGINT' } if sigNames[signal] != nil sigName = sigNames[signal] else sigName = 'signal '+signal end if !dryRun print 'Sending '+sigName+' to the following processes:'+"\n\n" end # Go through each line in the 'ps' output wines.each do |line| # True if we *require* /proc to be read for this process. # This is for cases where, judging by the process name we shouldn't # kill that process, but it's close enough to something we could kill # that we want to check if /proc can provide a more definitive answer. # (helps kill defunct processes) requireProc = false line.chomp! # Ignore the header if line =~ /USER\s*PID/ next end pid = String.new(line) name = String.new(line) # Parse the PID pid.sub!(/^\S+\s*(\d+)\s+.*/,'\1') # Parse the name name.sub!(/^\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+/,'') # If either of these are true some part of the parsing failed, and we # should thus simply skip this line if pid == name || pid == line || name == line next end # Ignore processes that are not wine-related if name =~ /(wine|windows|[A-Za-z]:\\|\.(exe|dll))/i # If wine is not a part of the process name, check if the # name either includes a *:\ (which most likely means it is a windows # path because it has already matched one of the above) or exe/dll. if name !~ /wine/i && (name !~ /[A-Za-z]:\\/ || name !~ /\.(exe|dll)/i) requireProc = true end else next end # At least on Linux kernels, /proc/ will include useful info, we use it to # check if the exe actually is wine # /exe test if File.readable?('/proc/'+pid+'/exe') and File.symlink?('/proc/'+pid+'/exe') if File.readlink('/proc/'+pid+'/exe') !~ /\/[^\/]*wine[^\/]*$/i next end # /cmdline test (for when /exe isn't readable) elsif File.readable?('/proc/'+pid+'/cmdline') cmdline = IO.read('/proc/'+pid+'/cmdline') cmdline.sub!(/\0.*/,''); if cmdline !~ /\/[^\/]*wine[^\/]*$/i && cmdline !~ /^[A-Za-z]:\\[^\\]+\\/ next end elsif requireProc next end # Clean up the name name.sub!(/\s*$/,'') extra = getFromEnviron('/proc/'+pid+'/environ') if $wine && resolveWine($wine) != extra['wine'] next elsif $bottle && $bottle != extra['bottle'] next end resolvedWine = resolveWine(extra['wine']) # Output information wineResult.push({ 'wine' => resolvedWine, 'bottle' => extra['bottle'], 'pid' => pid, 'name' => name }) if resolvedWine.length > wineLength wineLength = resolvedWine.length end end # The format of the output format = "%-"+wineLength.to_s+"s %-12s %-8s %-20s\n" # Header printf(format,'Wine','Bottle','PID','Command') # Display or send signals to each entry wineResult.each do |entry| printf(format,entry['wine'],entry['bottle'],entry['pid'],entry['name']) # Send SIGTERM (or output information if in dryRun mode) if !dryRun begin Process.kill(signal,entry['pid'].to_i) rescue puts('Error while attempting to send signal to '+entry['pid']+': '+$!) end end end if wineResult.length < 0 puts 'No wine processes found.' end end
Purpose: Output a list of Proton bottles
# File wwine, line 1538 def listProtonBottles () outFormat = '%-15s: %s'+"\n" bottles = getProtonBottles() begin printf(outFormat,'Bottle','Description') bottles.each do |bottle,meta| printf(outFormat,'steam:'+bottle,meta['name']) vputs(V_DBG,'steam:'+bottle+' is at '+meta['directory']) end rescue => ex handleException(ex) end end
Purpose: List available wine versions
# File wwine, line 982 def listWines(debug = false) outFormat = '%-30s: %s'+"\n" begin defaultWine,defaultWCMD = getAutoWithParams(nil,false) if !debug puts('Available wine versions:') end if defaultWine == nil defaultWine = '(no wine found)' end if defaultWine != 'wine' printf(outFormat,'(default wine)',defaultWine) end if inPath('wine') wineVer = getWineVersion() printf(outFormat,'wine',wineVer) if defaultWine == 'wine' && !debug puts " (default when no --wine is specified)" end end if $wine != nil and File.executable?($wine) and not File.directory?($wine) otherWver = getWineVersion($wine) printf(outFormat,'--wine',otherWver+' ('+$wine+')') end cxgBin = getCXwithParams('cxgames',nil,false) cxgVer = getCXVersionsFrom(cxgBin,true) cxoBin = getCXwithParams('cxoffice',nil,false) cxoVer = getCXVersionsFrom(cxoBin,true) if (cxgBin || !cxoBin || debug) printf(outFormat,'cxgames',cxgVer || '(not present)') if !debug puts " (aliases for cxgames: cxg, cxgames)" end end if cxoVer || debug printf(outFormat,'crossover',cxoVer) if !debug if !cxgBin puts " (aliases for crossover: cx, cxo, cxg, cxoffice, cxgames)" else puts " (aliases for crossover: cx, cxo, cxoffice)" end end end cedegaVer = getGameTreeVersionString() if cedegaVer || debug printf(outFormat,'gametree',cedegaVer || '(not present)') if ! debug puts " (aliases for gametree: cedega)" end end Dir.glob(ENV['HOME']+'/.PlayOnLinux/wine/linux-*/*') do |dir| if File.executable?(dir+'/bin/wine') name = dir.sub(/^.*\/([^\/]+)/,'\1') printf(outFormat,'pol-'+name,getWineVersion(dir+'/bin/wine')) end end Dir.glob(ENV['HOME']+'/.local/share/lutris/runners/wine/*') do |dir| if File.executable?(dir+'/bin/wine') name = dir.sub(/^.*\/([^\/]+)/,'\1') printf(outFormat,'lutris-'+name,getWineVersion(dir+'/bin/wine')) end end protons = getProtons() protons.each do |entry,path| version = entry.sub(/^proton-/,'') printf(outFormat,'proton-'+version,getWineVersion(path+'/dist/bin/wine')) end if $wine != nil && debug version = nil if File.directory?($wine) cxBin = getCXwithParams($wine,nil,false) version = getCXVersionsFrom(cxBin,true) elsif File.executable?($wine) version = getWineVersion($wine) end if version != nil printf(outFormat,$wine,version) end end rescue => ex handleException(ex) end end
Purpose: Load wwine data from a wrapper script
# File wwine, line 570 def loadWwineDataFromFile (file,dryRun = false) begin if !File.exists?(file) puts file+': does not exist' exit(1) elsif ! File.readable?(file) puts file+': is not readable' exit(1) end # Don't output any info-messages when in --env mode if $envMode outFormat = '' else puts 'Loading settings from '+file+':' outFormat = '%-20s: %s'+"\n" end source = File.open(file) if $wrapperPath outMessage = '' else outMessage = 'Using ' end info = Hash.new source.each do |line| if line.match(/^#\s+wwine/) key = line.sub(/^#\s+wwine(\S+):\s*(.+)$/,'\1').chomp value = line.sub(/^#\s+wwine(\S+):\s*(.+)$/,'\2').chomp info[key] = value end end if ! info['Info'] puts "The wwine header in this file is broken or missing." puts "Unable to continue." exit(0) end info['Version'] = info['Info'].sub(/^\s*v(\d+)\s*.*/,'\1').to_i if info['Version'] > 1 && info['Version'] <= 6 vputs(V_DBG,'File contains wwine metadata '+info['Info']) info = parseWwineData(info); elsif info['Version'] == 1 puts "ERROR: Unsupported wwine metadata format (v1)" puts "" puts "This format is no longer supported. You will have to create a new wrapper script" puts "using --wrap." exit(1) else version = info['Info'].sub(/^(v\S+).+/,'\1').chomp puts "This version of wwine supports info format v2, v3, v4, v5 and v6." puts "This file is version "+version puts "Unable to continue." exit(0) end if info['Cmd'].length < 1 puts "The wwine header in this file is broken." puts "Unable to continue." exit(0) end # Get the directory if info['Dir'] != 'nil' && !$wrapperCwd && !$envMode if $wrapperPath switchMessage = '--wrapdir' else switchMessage = 'Switching to directory' end printf(outFormat,switchMessage,info['Dir']) if File.exists?(info['Dir']) if $wrapperPath $wrapperCwd = info['Dir'] else Dir.chdir(info['Dir']) end else puts info['Dir']+': does not exist, giving up.' exit(0) end end # Get the --wine to use if $wine == nil && info['Wine'] != 'nil' $wine = info['Wine'] # Convert Cx*installdir to $wine if present in v2/v3 files if info['Version'] < 4 # Support for legacy Cx*installdir if info['Cxinstalldir'] != 'nil' && info['Cxinstalldir'] != nil vputs(V_DBG,'Converting Cxinstalldir entry to a Wine entry') $wine = info['Cxinstalldir'] end if info['CxGamesinstalldir'] != 'nil' && info['CxGamesinstalldir'] != nil vputs(V_DBG,'Converting CxGamesinstalldir entry to a Wine entry') $wine = info['CxGamesinstalldir'] end end printf(outFormat,outMessage+'--wine',$wine) end # Get the --bottle to use if $bottle == nil && info['Bottle'] != 'nil' $bottle = info['Bottle'] printf(outFormat,outMessage+'--bottle',$bottle) end # Set wineArch if info['Arch'] if info['Arch'] == 'win32' || info['Arch'] == 'win64' $wineArch = info['Arch'] printf(outFormat,outMessage+'--arch ',$wineArch) else puts('WARNING: Arch: '+info['Arch']+' is invalid, ignoring!') end end # Enable GameMode if needed if info['GameMode'] && info['GameMode'] == 'true' $preloadGameMode = true # FIXME: Drop the 'true' printf(outFormat,outMessage+'--gamemode','true') end # Set the command to run if ! dryRun && !$envMode && !$killMode if $wrapperPath out = 'Program command' else out = 'Will now run' end printf(outFormat,out,info['Cmd'].join(' ')) ARGV.unshift(*info['Cmd']) end rescue => ex handleException(ex) end end
Parses wwine metadata version 2+ (wwine 0.2+)
# File wwine, line 713 def parseWwineData (data) cmd = [] currNo = 0 while currNo < 999 if data['Cmd['+currNo.to_s+']'] != nil cmd.push(data['Cmd['+currNo.to_s+']']) else break end currNo += 1 end data['Cmd'] = cmd return data end
Purpose: Print formatted –help output Usage: printHelp('-shortoption', '–longoption', 'description');
Description will be reformatted to fit within a normal terminal
# File wwine, line 253 def printHelp (short,long,description) maxlen = 80 optionlen = 26 # Check if the short/long are LONGER than optionlen, if so, we need # to do some additional magic to take up only $maxlen. # The +1 here is because we always add a space between them, no matter what if (short.length + long.length + 1) > optionlen optionlen = short.length + long.length + 1; end generatedDesc = '' currdesc = '' description.split(/ /).each do |part| if(generatedDesc.length > 0) if (currdesc.length + part.length + 1 + 26) > maxlen generatedDesc.concat("\n") currdesc = '' else currdesc.concat(' ') generatedDesc.concat(' ') end end currdesc.concat(part) generatedDesc.concat(part) end if !(generatedDesc.length > 0) raise("Option mismatch") end generatedDesc.split(/\n/).each do |descr| printf("%-4s %-21s %s\n",short,long,descr) short = ''; long = '' end end
Purpose: Resolve a wine to its proper name
# File wwine, line 1775 def resolveWine(wine) map = Hash.new() map['cx'] = 'crossover' map['cxo'] = 'crossover' map['cxoffice'] = 'crossover' map['cxg'] = 'cxgames' map['cedega'] = 'gametree' if(map[wine]) wine = map[wine] end if wine =~ /\.local\/share\/lutris/ remapped = wine.sub!(/^.*\.local\/share\/lutris\/runners\/wine\/([^\/]+).*/,'\1') return 'lutris-'+remapped end return wine end
Purpose: Run the wine specified, using the bottle specified and arguments specified
# File wwine, line 1603 def runWine (wine,bottle,args) cmd = nil type = nil if bottle =~ /^steam:/ bottleID = bottle.sub(/^steam:(\d+)/,'\1') protonBottles = getProtonBottles() if protonBottles[bottleID] bottle = protonBottles[bottleID]['directory'] else puts "Unable to find a steam bottle with the ID \""+bottleID+"\"" exit 1 end end if wine == nil type,cmd = getAutoWithParams(bottle) wine = type else # Expand cx* wine = resolveWine(wine) type = wine if File.directory?(wine) if File.executable?(wine+'/bin/cxstart') vputs(V_DBG,wine+': looks like a cx directory, assuming type=cx') type = 'crossover' end end if type == 'crossover' || wine == 'cxgames' || wine == 'cxoffice' || wine == 'crossover' type = 'crossover' missingIsFatal = true if wine == 'cxgames' missingIsFatal = false end cmd = getCXwithParams(wine,bottle,missingIsFatal) if wine == 'cxgames' && cmd == nil cmd = getCXwithParams('crossover',bottle) if cmd != nil puts('Converting --wine cxgames to --wine crossover since cxgames is not installed') end end elsif wine =~ /^wine(64|32|-unstable(32|64)?)?$/ || ( File.executable?(wine) && !File.directory?(wine) ) type = 'wine' cmd = getWineWithParams(wine,bottle) elsif resolveWine(wine) == 'gametree' cmd = getGameTreeWithParams(wine,bottle) elsif wine =~ /^(pol|lutris)[-:]?/i realWine = wine.sub(/^(pol|lutris)[-:]?/i,'') cmd = getCustomwithParams(realWine,bottle,wine) elsif wine =~ /^proton-/ cmd,bottle = getProtonWithParams(wine,bottle,true,true) else puts('Unknown --wine: '+wine) if File.directory?(wine) puts(wine+' does not appear to be the path to a crossover installation directory') puts('') end puts('Must be one of the wine versions listed by `wwine --list` or the path to either a') puts('wine executeable or a crossover installation directory') exit(1) end end if cmd == nil puts('Command detection failed. This is a bug') exit(1) end if wine =~ /^proton-/ && args[0] !~ /(\/|\\)/ && !File.exists?(args[0]) orig = args[0] ['syswow64','system32'].each do |subdir| if File.exists?(bottle+'/drive_c/windows/'+subdir+'/'+args[0]+'.exe') orig = args[0] args[0] = bottle+'/drive_c/windows/'+subdir+'/'+args[0]+'.exe' vputs(V_DBG,'Resolving for Proton: '+orig+' -> '+orig+'.exe'+' -> '+args[0]) break elsif File.exists?(bottle+'/drive_c/windows/'+subdir+'/'+args[0]) orig = args[0] args[0] = bottle+'/drive_c/windows/'+subdir+'/'+args[0] vputs(V_DBG,'Resolving for Proton: '+orig+' -> '+args[0]) break end end if args[0] != orig vputs(V_INFO,'Proton can\'t find Wine commands, therefore '+orig+' is converted to '+args[0]) puts 'wwine: Resolved '+orig+' to '+args[0] end end cmd.concat(args) # If args is a single parameter, that parameter is --version, and we're not using # vanilla wine, then we emulate vanilla wine's --version output. This helps # utilities like winetricks when it is running under "wwine --env" if args.length == 1 && args[0] == '--version' emulateVersionOutput(wine) exit end # If we're in envMode, then we let setEnvAndExec handle the rest if $envMode setEnvAndExec(wine,bottle,cmd,args,type) # If wrapperPath is set, then we're suppose to generate a wrapper script instead # of actually executing it. elsif $wrapperPath != nil generateWrapper(wine,bottle,$wrapperPath,cmd,args,type) else wine = resolveWine(wine) envPut('__WWINE_INT_XWINE',wine); if bottle envPut('__WWINE_INT_XBOTTLE',bottle); end if $wineArch envPut('WINEARCH',$wineArch) end if $preloadGameMode preload = '/usr/$LIB/libgamemodeauto.so' if ENV['LD_PRELOAD'] preload = preload+':'+ENV['LD_PRELOAD'] end envPut('LD_PRELOAD',preload) end runcmd(cmd) end end
Purpose: Run a command, outputting info of it in verbose mode
# File wwine, line 729 def runcmd(cmd, type = 'exec') if(type == 'exec') vputs(V_INFO,'Executing: '+cmd.join(' ')) exec *cmd else vputs(V_INFO,'Running: '+cmd.join(' ')) return system(*cmd) end end
Purpose: Sets WINE and WINEPREFIX, and then either outputs them to STDOUT, or
executes whatever native command was supplied (the logic that handles --env)
# File wwine, line 509 def setEnvAndExec (wine,bottle,cmd,args,type) if wine == nil type,cmd = getAutoWithParams(bottle) end envPut('__WWINE_INT_WINE',wine) if type == 'crossover' cxdir = getCXwithParams(wine,bottle,true,false,true) if File.executable?(cxdir+'/bin/wineserver') envPut('WINESERVER',cxdir+'/bin/wineserver') end end if !bottle puts("--env requires a --bottle") exit(1) end envPut('__WWINE_INT_BOTTLE',bottle); envPut('__WWINE_INT_ENVMODE',$version) if $verbosity > 0 envPut('__WWINE_INT_VERBOSITY',$verbosity.to_s) end if type == 'wine' || wine == 'wine' || (File.exists?(wine) && !File.directory?(wine)) || wine =~ /^(pol|lutris)[-:]?/i vputs(V_DBG,"Used ENV/WINEPREFIX method to get bottle") bottlePath = ENV['WINEPREFIX'] elsif type == 'proton' || wine =~ /^proton-/ vputs(V_DBG,"Used STEAM_COMPAT_DATA_PATH method to get bottle") bottlePath = ENV['STEAM_COMPAT_DATA_PATH'] if File.exists?(bottlePath+'/pfx') bottlePath = bottlePath+'/pfx' end elsif resolveWine(wine) == 'gametree' vputs(V_DBG,"Used ENV/cedega method to get bottle") bottlePath = ENV['HOME']+'/.cedega/'+bottle else vputs(V_DBG,"Used CX method to get bottle") bottlePath = getCXwithParams(wine,bottle,true,true) end if $wineArch envPut('WINEARCH',$wineArch) envPut('__WWINE_INT_WINEARCH',$wineArch) end if bottlePath == nil puts "ERROR: setEnvAndExec() has no bottlePath" exit 1 end envPut('WINEPREFIX',bottlePath) envPut('WINE',File.expand_path($0)) if args.length > 0 puts("Executing "+args.join(' ')) exec *args else puts("WINE="+shellQuote(ENV['WINE'])) puts("WINEPREFIX="+shellQuote(ENV['WINEPREFIX'])) end exit end
Purpose: Quote a string (or array of strings) for use in the shell
# File wwine, line 289 def shellQuote(target) if target.class == Array newTarget = [] target.each do |subt| subt = shellQuote(subt) newTarget.push(subt) end return newTarget end fixedTarget = target.gsub('\\') { '\\\\' } fixedTarget.gsub!(/'/,"'\"'\"'") finalTarget = "'" finalTarget.concat(fixedTarget) finalTarget.concat("'"); return finalTarget end
Purpose: Show the manpage
# File wwine, line 801 def showManPage () if ! inPath('man') puts puts "You don't appear to have the 'man' program installed." puts "Please install it, then re-run wwine --man" exit(0) end mySelf = File.expand_path($0) while File.symlink?(mySelf) mySelf = File.readlink(mySelf) end sourceDir = '.' if mySelf != nil sourceDir = File.dirname(mySelf) end dirs = [sourceDir] if ENV['MANPATH'] dirs.concat(ENV['MANPATH'].split(':')) end dirs.push('./') dirs.each do |dir| [ 'wwine.1','man1/wwine.1','man1/wwine.1.gz','man1/wwine.1.bz2','man1/wwine.1.lzma'].each do |manFile| if File.exists?(dir+'/'+manFile) exec('man',dir+'/'+manFile) end end end puts puts 'wwine failed to locate its manpage.' puts 'Run the following command to view it:' puts '\curl -s "http://github.com/zerodogg/wwine/raw/master/wwine.1" | groff -T utf8 -man | \less' end
Purpose: Unquote a string generated by shellQuote
# File wwine, line 309 def unShellQuote(target) fixedTarget = target.gsub("\\'","'") fixedTarget.gsub!('\\\\') { '\\' } return fixedTarget end
Purpose: Output a string if in verbose mode
# File wwine, line 750 def vputs (level,str) if(level == V_DBG) str = 'Debug: '+str end if $verbosity >= level # Don't enable debugging output at all if STDOUT is not a tty and we're # running in env mode. In these cases the caller might rely upon parsing # specific output, and including debugging info can cause the caller to fail. if !STDOUT.isatty && ENV['__WWINE_INT_ENVMODE'] == $version return end puts(str) end end
Purpose: Write a wrapper script
# File wwine, line 316 def writeWrapper (targetFile, wwineCommand, otherCommand, cwd, wwineInfo, args, wineprefix = nil) # Open the file for writing begin file = File.open(targetFile,'w') rescue puts('Failed to open "'+targetFile+'" for writing: '+$!) exit(1) end # --gamemode requires v6, --arch requires v5, otherwise we are v4 compatible if $preloadGameMode metaFormat = 'v6' elsif $wineArch metaFormat = 'v5' else metaFormat = 'v4' end file.puts('#!/bin/sh') file.puts('# Script autogenerated by wwine '+$version+' (http://random.zerodogg.org/wwine)') file.puts('# Licensed under the GNU General Public License version 3 or later') file.puts('#') file.puts('# You should have received a copy of the GNU General Public License') file.puts('# along with wwine. If not, see <http://www.gnu.org/licenses/>.') file.puts('#') file.puts('# -- Begin wwine metadata --') file.puts('# wwineInfo: '+metaFormat) wwineInfo.each do |key,value| file.puts('# wwine'+key+': '+value) end subN = 0 args.each do |sub| file.puts("# wwineCmd["+subN.to_s+"]: "+sub) subN += 1 end file.puts('# -- End wwine metadata --') file.puts('') file.puts("cd "+shellQuote(cwd)+" || exit 1") file.puts('wwine='+shellQuote(File.expand_path($0))) file.puts('if [ ! -x "$wwine" ]; then') file.puts("\t"+'wwine="`which wwine 2> /dev/null`"') file.puts('fi') file.puts('if [ "x$wwine" != "x" ]; then') # _WWINE_SCRIPT is currently just used to detect if a wrapper script is # being used. But is intended to help wwine use '--from' automatically in # the future if for some reason there are backwards-incompatible changes to # one of the command-line parameters used by scripts. file.puts("\t"+'export _WWINE_SCRIPT="$0"') file.puts("\t"+'exec "$wwine" '+shellQuote(wwineCommand).join(" ")+' "$@"') file.puts('else') # Add wineprefix if needed. This is used when --wine is wine because wine # has no support for specifying a bottle on the command-line. if wineprefix != nil wineprefix.sub!(/'/,'\\') file.puts("\t"+'export WINEPREFIX='+shellQuote(wineprefix)) end if $wineArch file.puts("\t"+'export WINEARCH='+shellQuote($wineArch)) end if wwineInfo['Wine'] =~ /^(pol|lutris)/i type = 'POL' if wwineInfo['Wine'] =~ /^lutris/ type = 'lutris' end path = getCustomwithParams(wwineInfo['Wine'].sub(/^(pol|lutris)[-:]?/i,''),nil,type,false,true) if path == nil abort("getCustomwithParams returned nil wine. This is a bug") end if ENV['WINEDEBUG'] != nil file.puts("\t"+"export WINEDEBUG="+shellQuote(ENV['WINEDEBUG'])) end file.puts("\t"+'export WINESERVER='+shellQuote(path+'/bin/wineserver')) file.puts("\t"+'export WINELOADER='+shellQuote(path+'/bin/wine')) file.puts("\t"+'export WINEDLLPATH='+shellQuote(path+'/lib/wine')) elsif wwineInfo['Wine'] =~ /^proton/ file.puts("\t"+'export STEAM_COMPAT_DATA_PATH='+shellQuote(ENV['STEAM_COMPAT_DATA_PATH'])) elsif wwineInfo['Wine'] == 'wine' || wineprefix if ENV['WINEDEBUG'] != nil file.puts("\t"+"export WINEDEBUG="+shellQuote(ENV['WINEDEBUG'])) end end if $preloadGameMode file.puts("\t"+'export LD_PRELOAD="/usr/\$LIB/libgamemodeauto.so:$LD_PRELOAD"') end file.puts("\t"+"exec "+shellQuote(otherCommand).join(" ")+' "$@"') file.puts('fi') file.puts('echo "Launch script failed. No wwine found and wine command failed to exec."') file.puts('exit 1') file.chmod(0755) file.close puts('Wrote wrapper script to '+targetFile) end