From c127cf27ea873ec4271c5c8f99e122029fd98b36 Mon Sep 17 00:00:00 2001 From: Ryan Nixon Date: Mon, 16 Mar 2020 21:01:51 -0700 Subject: [PATCH] Initial commit --- .gitignore | 11 +-- README.md | 38 +++++++++- go.mod | 13 ++++ go.sum | 70 ++++++++++++++++++ main.go | 103 ++++++++++++++++++++++++++ steam.go | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ui.go | 78 ++++++++++++++++++++ 7 files changed, 510 insertions(+), 11 deletions(-) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 steam.go create mode 100644 ui.go diff --git a/.gitignore b/.gitignore index 66fd13c..8fec7bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,8 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Dependency directories (remove the comment below to include it) -# vendor/ +# The built binary if using `go build` +hwrc-proton-launcher \ No newline at end of file diff --git a/README.md b/README.md index 935a3ac..93f344b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ -# hwrc-proton-launcher -A launcher program for gamers running Homeworld Remastered on Linux +# Homeworld Remastered Proton Launcher + +This is a launcher application for the Steam version of Homeworld Remastered Collection. It can be used as a replacement for Gearbox's launcher which has Linux compatibility issues. + +Much thanks needs to be given to the version of the launcher at https://git.sr.ht/~_dev_fra/hwrc-proton-launcher which this code was originally a Go port of. + +## Running + +To get the application, download the binary from the Releases tab for your platform. + +The application may be run in two modes: +* Command Line: Takes all inputs from the CLI. Run `./hwrc-proton-launcher -help` for more information. +* GUI: A graphical interface for launching the games. Will display if no CLI options are passed by running `./hwrc-proton-launcher` + +## Known Issues + +I have only tested this on my machine; there are likely incompatibility problems on other variants. If you encounter an issue file it to this repository! + +I have also not yet tested the mod loading functionality. + +## Attribution + +This project is a port of https://git.sr.ht/~_dev_fra/hwrc-proton-launcher, converting it to a Go application and adding a UI...admittedly it an excuse for me to try out [Fyne](https://fyne.io/) by helping out the experience of running my favorite game :) + +## Contributing + +The tool has been written in Golang using the Fyne UI framework. The following items are required to be installed on your system in order to compile: + +* [Go 1.13+](https://golang.org/dl/) +* The [Fyne Prerequisites](https://fyne.io/develop/index.html) such as GCC and graphics library headers. + +Once installed, simply run the following to build and execute the application: + +```sh +go build && ./hwrc-proton-launcher +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f24bdd4 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/taiidani/hwrc-proton-launcher + +go 1.13 + +require ( + fyne.io/fyne v1.2.3 + github.com/kr/pretty v0.1.0 // indirect + github.com/mitchellh/go-ps v1.0.0 + github.com/sirupsen/logrus v1.4.2 + github.com/stretchr/testify v1.4.0 // indirect + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7ae6bf6 --- /dev/null +++ b/go.sum @@ -0,0 +1,70 @@ +fyne.io/fyne v1.2.3 h1:5xwtSBNjxxmg+GF/lYvvf4xPzyjgWQoJVrzb+bt5gaA= +fyne.io/fyne v1.2.3/go.mod h1:JhDdBrPP/Kdr1H5ZT3HW8E/6zlz+GkOldWqSirGBDnY= +github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= +github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= +github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM= +github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= +github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= +github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= +github.com/josephspurrier/goversioninfo v0.0.0-20190124120936-8611f5a5ff3f/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e h1:LJUrNHytcMXWKxnULIHPe5SCb1jDpO9o672VB1x2EuQ= +github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= +github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e h1:FFotfUvew9Eg02LYRl8YybAnm0HCwjjfY5JlOI1oB00= +github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..11ce643 --- /dev/null +++ b/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "flag" + "fmt" + "os" + + log "github.com/sirupsen/logrus" +) + +const ( + // defaultProtonVersion stores the most stable reported Proton version for Homeworld + defaultProtonVersion = "4.11" + + // applicationName matches the name of the binary produced that end-users will see + applicationName = "hwrc-proton-launcher" + + // Steam ID of Homeworld Remastered in the library + appID = 244160 +) + +var ( + // defaultSteamPaths contains a list of common locations that steam is found on a system + defaultSteamPaths = []string{ + "$HOME/.local/share/Steam", + "$HOME/.steam/steam", + } + + flagWindowed bool + flagModPath string + flagHelp bool + flagVerbose bool +) + +func main() { + flag.BoolVar(&flagWindowed, "w", false, "Windowed mode") + flag.BoolVar(&flagHelp, "help", false, "Display this help") + flag.BoolVar(&flagVerbose, "v", false, "Display verbose output, for debugging purposes") + flag.StringVar(&flagModPath, "m", "", "Load mod at the given `modPath` (see below for details)") + flag.Parse() + + if flagVerbose { + log.SetLevel(log.DebugLevel) + } + + var fn func(*steam) + if flagHelp { + help() + os.Exit(1) + } else if len(flag.Args()) > 0 { + fn = cli + } else { + fn = ui + } + + s, err := newSteam() + if err != nil { + log.Fatal(err) + } + fn(s) +} + +// help displays the help text for the application +func help() { + fmt.Println(applicationName + " [OPTIONS] [GAME]") + fmt.Println("") + + flag.PrintDefaults() + + fmt.Println(` +This is a launcher application for the Steam version of Homeworld Remastered Collection. It can be used as a replacement for Gearbox's launcher which has Linux compatibility issues. + +WARNING: Steam must be running already before running this tool. + +GAME: + + ` + fmt.Sprintf("%-10s", hw1cla) + ` Homeworld 1 Classic + ` + fmt.Sprintf("%-10s", hw2cla) + ` Homeworld 2 Classic + ` + fmt.Sprintf("%-10s", hw1rem) + ` Homeworld 1 Remastered + ` + fmt.Sprintf("%-10s", hw2rem) + ` Homeworld 2 Remastered + ` + fmt.Sprintf("%-10s", hwmp) + ` Homeworld Remastered Multiplayer + +Loading a mod: + +The option '-m' requires a path to the mod file. The path can be an absolute path, or a relative path to the game DATAWORKSHOPSMODS folder. The following folders should be available: +- /steamapps/common/Homeworld/HomeworldRM/DATAWORKSHOPMODS +- /steamapps/common/Homeworld/Homeworld2Classic/DATAWORKSHOPMODS + +For example, to load the Homeworld Remastered 2.3 Players Patch using a relative path (provided the mod file and any parent folder are in the DATAWORKSHOPSMODS folder): + + ` + applicationName + ` hw1rem -m 1190476337/2.3PlayersPatch.big`) +} + +// cli runs the game with the expectation that configuration is coming from the CLI arguments +func cli(s *steam) { + // Figure out the game arguments + game := flag.Arg(0) + + // Run the game! + if err := s.run(game); err != nil { + log.Fatal(err.Error()) + } +} diff --git a/steam.go b/steam.go new file mode 100644 index 0000000..9c251d2 --- /dev/null +++ b/steam.go @@ -0,0 +1,208 @@ +package main + +import ( + "fmt" + "os" + "path" + "strings" + "syscall" + + ps "github.com/mitchellh/go-ps" + log "github.com/sirupsen/logrus" +) + +type steam struct { + clientPath string + protonPath string + gameRootPath string +} + +const ( + hw1cla = "hw1cla" + hw2cla = "hw2cla" + hw1rem = "hw1rem" + hw2rem = "hw2rem" + hwmp = "hwmp" +) + +// newSteam will create a new steam client interface and verify that Steam is currently running +func newSteam() (*steam, error) { + var err error + ret := &steam{} + + // Discover Steam and Proton + ret.clientPath, err = findSteam() + if err != nil { + return ret, err + } + ret.protonPath = fmt.Sprintf("%s/steamapps/common/Proton %s", ret.clientPath, defaultProtonVersion) + ret.gameRootPath = fmt.Sprintf("%s/steamapps/common/Homeworld", ret.clientPath) + + return ret, nil +} + +// run will run the game! +func (s *steam) run(game string) error { + gamePath, args := getSteamArguments(game, flagWindowed, flagModPath, s.gameRootPath) + + // Change directory to the location of the executable + if err := os.Chdir(gamePath); err != nil { + log.Fatalf("Unable to change directory to game path: %s", err) + } + + binary := fmt.Sprintf("%s/dist/bin/wine", s.protonPath) + args = append([]string{binary, "steam.exe"}, args...) + env := getSteamEnvironment(s.clientPath, s.protonPath) + + return syscall.Exec(binary, args, env) +} + +// findSteam will locate the steam directory on disk in order to find the Homeworld binaries +// It will also determine if Steam is running or not, which is required for the app to execute. +// +// TODO: If not found, prompt the user to fill in the missing path +func findSteam() (string, error) { + steamPath := "" + for _, path := range defaultSteamPaths { + resolvedPath := os.ExpandEnv(path) + log.Debug("Checking " + resolvedPath + " for Steam") + if _, err := os.Stat(resolvedPath); err == nil { + steamPath = resolvedPath + break + } + } + if steamPath == "" { + return steamPath, fmt.Errorf("Unable to find Steam location") + } + + // Is Steam running? + processes, err := ps.Processes() + if err != nil { + return steamPath, err + } + + steamFound := false + for _, process := range processes { + if process.Executable() == "steam" { + steamFound = true + } + } + + if !steamFound { + return steamPath, fmt.Errorf("Steam does not appear to be running") + } + + return steamPath, nil +} + +// getSteamArguments will determine the exact arguments to pass to steam in order to have the game run successfully +func getSteamArguments(game string, windowed bool, modPath string, gameRootPath string) (string, []string) { + var gameExe string + gameOptions := []string{} + + switch game { + case hw1cla: + gameExe = fmt.Sprintf("%s/Homeworld1Classic/exe/Homeworld.exe", gameRootPath) + gameOptions = append(gameOptions, "/noglddraw") + + if windowed { + gameOptions = append(gameOptions, "/window") + } + case hw2cla: + gameExe = fmt.Sprintf("%s/Homeworld2Classic/Bin/Release/Homeworld2.exe", gameRootPath) + + if windowed { + gameOptions = append(gameOptions, "/windowed") + } + + case hw1rem: + gameExe = fmt.Sprintf("%s/HomeworldRM/bin/Release/HomeworldRM.exe", gameRootPath) + gameOptions = append(gameOptions, "-dlccampaign HW1Campaign.big") + gameOptions = append(gameOptions, "-campaign HomeworldClassic") + gameOptions = append(gameOptions, "-moviepath DataHW1Campaign") + if windowed { + gameOptions = append(gameOptions, "/windowed") + } + + case hw2rem: + gameExe = fmt.Sprintf("%s/HomeworldRM/bin/Release/HomeworldRM.exe", gameRootPath) + gameOptions = append(gameOptions, "-dlccampaign HW2Campaign.big") + gameOptions = append(gameOptions, "-campaign Ascension") + gameOptions = append(gameOptions, "-moviepath DataHW2Campaign") + if windowed { + gameOptions = append(gameOptions, "/windowed") + } + + case hwmp: + gameExe = fmt.Sprintf("%s/HomeworldRM/bin/Release/HomeworldRM.exe", gameRootPath) + if windowed { + gameOptions = append(gameOptions, "/windowed") + } + + default: + help() + os.Exit(1) + } + + if modPath != "" { + gameOptions = append(gameOptions, "-workshopmod "+modPath) + } + + return path.Dir(gameExe), append([]string{gameExe}, gameOptions...) +} + +// getSteamEnvironment provides the environment variables required by Steam +// +// WARNING: This section should be periodically checked and updated when new versions of Steam and Proton are released. +// +// These environment variables are set by Steam when launching the game from the Steam client using Steam Play. To retrieve +// these variables inspect the run script that can be dumped from Steam in this way: +// 1. Set the game launch options in the Steam client to +// "PROTON_DUMP_DEBUG_COMMANDS=1 %command%"; +// 2. Launch the game (even if it does not work); +// 3. Find the script "/tmp/proton_/run". +func getSteamEnvironment(steamPath, protonPath string) []string { + steamCustomPaths := strings.Join([]string{ + fmt.Sprintf("%s/dist/bin", protonPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/amd64/bin", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/amd64/usr/bin/", steamPath), + os.Getenv("PATH"), + }, ":") + + ldLibraryPath := strings.Join([]string{ + fmt.Sprintf("%s/dist/lib64", protonPath), + fmt.Sprintf("%s/dist/lib", protonPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/pinned_libs_32", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/pinned_libs_64", steamPath), + "/usr/lib/x86_64-linux-gnu/libfakeroot", + "/lib/i386-linux-gnu", + "/usr/local/lib", + "/lib/x86_64-linux-gnu", + "/lib", + "/lib/i386-linux-gnu/sse2", + "/lib/i386-linux-gnu/i686", + "/lib/i386-linux-gnu/i686/sse2", + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/i386/lib/i386-linux-gnu", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/i386/lib", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/i386/usr/lib/i386-linux-gnu", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/i386/usr/lib", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/amd64/lib/x86_64-linux-gnu", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/amd64/lib", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/amd64/usr/lib/x86_64-linux-gnu", steamPath), + fmt.Sprintf("%s/ubuntu12_32/steam-runtime/amd64/usr/lib", steamPath), + }, ":") + + return append(os.Environ(), []string{ + "PATH=" + steamCustomPaths, + "TERM=xterm", + "WINEDEBUG=-all", + fmt.Sprintf("WINEDLLPATH=%s/dist/lib64/wine:%s/dist/lib/wine", protonPath, protonPath), + "LD_LIBRARY_PATH=" + ldLibraryPath, + fmt.Sprintf("WINEPREFIX=%s/steamapps/compatdata/%d/pfx/", steamPath, appID), + "WINEESYNC=1", + fmt.Sprintf("SteamGameId=%d", appID), + fmt.Sprintf("SteamAppId=%d", appID), + "WINEDLLOVERRIDES=steam.exe=b;mfplay=n;d3d11=n;d3d10=n;d3d10core=n;d3d10_1=n;dxgi=n", + fmt.Sprintf("STEAM_COMPAT_CLIENT_INSTALL_PATH=%s", steamPath), + }...) +} diff --git a/ui.go b/ui.go new file mode 100644 index 0000000..7a088bb --- /dev/null +++ b/ui.go @@ -0,0 +1,78 @@ +package main + +import ( + "fyne.io/fyne/app" + "fyne.io/fyne/widget" + log "github.com/sirupsen/logrus" +) + +// ui runs the game with the expectation that configuration is provided by the GUI +func ui(s *steam) { + app := app.New() + + // Expose the windowed flag to the UI + windowed := widget.NewCheck("Windowed", func(checked bool) { + flagWindowed = checked + }) + windowed.SetChecked(flagWindowed) + + // Expose the modPath flag to the UI + modPath := widget.NewEntry() + modPath.SetText(flagModPath) + modPath.SetPlaceHolder("Path to Mod") + + // Build it! + w := app.NewWindow("HWRM Launcher") + w.SetContent(widget.NewVBox( + widget.NewLabel("Homeworld Remastered Launcher"), + widget.NewGroup("Classic", + widget.NewHBox( + widget.NewButton("Homeworld 1", func() { + flagModPath = modPath.Text + if err := s.run(hw1cla); err != nil { + log.Fatal(err.Error()) + } + }), + widget.NewButton("Homeworld 2", func() { + flagModPath = modPath.Text + if err := s.run(hw2cla); err != nil { + log.Fatal(err.Error()) + } + }), + ), + ), + widget.NewGroup("Remastered", + widget.NewHBox( + widget.NewButton("Homeworld 1", func() { + flagModPath = modPath.Text + if err := s.run(hw1rem); err != nil { + log.Fatal(err.Error()) + } + }), + widget.NewButton("Homeworld 2", func() { + flagModPath = modPath.Text + if err := s.run(hw2rem); err != nil { + log.Fatal(err.Error()) + } + }), + ), + ), + widget.NewGroup("Multiplayer", + widget.NewButton("Homeworld Remastered", func() { + flagModPath = modPath.Text + if err := s.run(hwmp); err != nil { + log.Fatal(err.Error()) + } + }), + ), + widget.NewGroup("Options", + windowed, + widget.NewHBox( + widget.NewLabel("Mod Path"), + modPath, + ), + ), + )) + + w.ShowAndRun() +}