package main import ( "fmt" "os" "path/filepath" "s1d3sw1ped/swipedmodswitcher/admin" "slices" "strings" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "gopkg.in/yaml.v3" ) type Config struct { ModProfilesDirectory string `yaml:"mod_profiles_dir"` GameModsDirectory string `yaml:"game_mods_dir"` } func saveConfig(cfg Config) { d, err := yaml.Marshal(cfg) if err != nil { fmt.Println(err) } err = os.WriteFile("config.yaml", d, 0775) if err != nil { fmt.Println(err) } } func loadConfig() *Config { _, err := os.Stat("config.yaml") if err != nil { fmt.Println(err) saveConfig(Config{ModProfilesDirectory: "", GameModsDirectory: ""}) } d, err := os.ReadFile("config.yaml") if err != nil { fmt.Println(err) } cfg := Config{} err = yaml.Unmarshal(d, &cfg) if err != nil { fmt.Println(err) return &Config{} } return &cfg } var cfg *Config func main() { if !admin.Admin() { if err := admin.RunSelfElevated(); err != nil { panic(err) } os.Exit(0) } cfg = loadConfig() ap := app.New() wp := ap.NewWindow("Swiped Mod Switcher") wp.Resize(fyne.NewSize(600, 1)) wp.CenterOnScreen() grid := container.NewVBox() scroll := container.NewVScroll(grid) scroll.SetMinSize(fyne.NewSize(-1, 400)) //load grid fillGrid(grid, wp) profilesrootentry := widget.NewEntry() profilesrootentry.SetText(cfg.ModProfilesDirectory) profilesrootentry.Disable() archiverootentry := widget.NewEntry() archiverootentry.SetText(cfg.GameModsDirectory) archiverootentry.Disable() container := container.NewVBox( widget.NewForm(widget.NewFormItem("Mod Profiles Directory", profilesrootentry)), widget.NewButtonWithIcon("Select", theme.FolderIcon(), func() { dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) { if err != nil { fmt.Println(err) } if lu == nil { return } profilesrootentry.SetText(lu.Path()) cfg.ModProfilesDirectory = lu.Path() saveConfig(*cfg) fillGrid(grid, wp) }, wp).Show() }), widget.NewForm(widget.NewFormItem("Game Mod Directory", archiverootentry)), widget.NewButtonWithIcon("Select", theme.FolderIcon(), func() { dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) { if err != nil { fmt.Println(err) } if lu == nil { return } archiverootentry.SetText(lu.Path()) cfg.GameModsDirectory = lu.Path() saveConfig(*cfg) fillGrid(grid, wp) }, wp).Show() }), widget.NewSeparator(), widget.NewButton("Reload Mod Profiles", func() { fillGrid(grid, wp) }), scroll, ) wp.SetContent(container) wp.ShowAndRun() } func fillGrid(grid *fyne.Container, parent fyne.Window) { profiles := scanProfiles(cfg.ModProfilesDirectory) archive := cfg.GameModsDirectory grid.Hide() defer grid.Show() grid.RemoveAll() for _, profile := range profiles { profilename := filepath.Base(profile) grid.Add(widget.NewButton(profilename, makeIFunc(profile, archive, parent))) } } func scanProfiles(profilesroot string) []string { dir := filepath.Join(profilesroot, "*/") m, err := filepath.Glob(dir) if err != nil { fmt.Println(err) } mm := []string{} for _, path := range m { if s, err := os.Stat(path); err == nil && !strings.HasPrefix(s.Name(), ".") && s.IsDir() { mm = append(mm, path) } } slices.SortStableFunc(mm, func(a, b string) int { return strings.Compare(a, b) }) return mm } func makeIFunc(profile, archive string, parent fyne.Window) func() { return func() { if cfg.GameModsDirectory == "" || cfg.ModProfilesDirectory == "" { dialog.NewInformation("Notice", "you must select a game mod directory and mod profiles directory before", parent).Show() return } // delete any existing archive folder _ = os.RemoveAll(archive) // link the profile to the archive err := os.Symlink(profile, archive) if err != nil { dialog.NewInformation("Error", "error linking most likely issue is Swiped Mod Switcher is not running as admin", parent).Show() return } dialog.NewInformation("Success", fmt.Sprintf("filinked files from %s to %s", filepath.Base(profile), archive), parent).Show() } }