package modlist import ( "encoding/json" "os" "regexp" "strings" "github.com/charmbracelet/log" ) // fileRegex matches const fileRegex string = "([\\w\\s-]+)_([0-9.]+)\\.zip" /// Modlist types and related functions. // Mod holds data about a single mod. type Mod struct { Name string `json:"name"` Enabled bool `json:"enabled"` } // Modlist holds a slice of mods. type Modlist struct { Mods []Mod `json:"mods"` } /// Non exported functions func (modlist *Modlist) setModStatus(name string, status bool) { for i := range modlist.Mods { if modlist.Mods[i].Name == name && modlist.Mods[i].Enabled != status { modlist.Mods[i].Enabled = status log.Debugf("Setting status of mod %s to %v", modlist.Mods[i].Name, modlist.Mods[i].Enabled) } } } func (modlist *Modlist) setAllStatus(status bool) { for i := range modlist.Mods { modlist.Mods[i].Enabled = status log.Debugf("Setting status of mod %s to %v", modlist.Mods[i].Name, modlist.Mods[i].Enabled) } } // EnableMod enables the given mod. func (modlist *Modlist) EnableMod(name string) { modlist.setModStatus(name, true) } // EnableMods enables the given mods. func (modlist *Modlist) EnableMods(names ...string) { for _, name := range names { modlist.EnableMod(name) } } // DisableMod disables the given mod. func (modlist *Modlist) DisableMod(name string) { modlist.setModStatus(name, false) } // DisableMods disables the given mods. func (modlist *Modlist) DisableMods(names ...string) { for _, name := range names { modlist.DisableMod(name) } } // EnableAll enables all mods. func (modlist *Modlist) EnableAll() { modlist.setAllStatus(true) } // DisableAll disables all mods. func (modlist *Modlist) DisableAll() { modlist.setAllStatus(false) } // HasMod reports if a modlist has a mod with name `s` func (modlist *Modlist) HasMod(s string) bool { for _, mod := range modlist.Mods { if mod.Name == s { return true } } return false } // HasMod reports if a modlist has an enabled mod with name `s` func (modlist *Modlist) HasModEnabled(s string) bool { for _, mod := range modlist.Mods { if mod.Name == s && mod.Enabled { return true } } return false } // Print out each mod and its status. func (modlist *Modlist) Print(prefix string) { log.Info(prefix + ": {") for _, mod := range modlist.Mods { var endis string // I wish go had a ternary so this could all be in one line if mod.Enabled { endis = "en" } else { endis = "dis" } log.Infof("\t%s: %sabled,", mod.Name, endis) } log.Info("}") } func (modlist *Modlist) String() { modlist.Print("mods") } /// Functions related to the modlist but that shouldn't be attached to the types // WriteToFile writes the modlist to the given filename. func WriteToFile(filename string, mods *Modlist) error { data, err := json.MarshalIndent(&mods, "", " ") if err != nil { return err } os.WriteFile(filename, data, os.FileMode(0744)) return nil } // ReadFromFile reads the modlist from the given filename. func ReadFromFile(filename string) (Modlist, error) { var mods Modlist = Modlist{} data, err := os.ReadFile(filename) if err != nil { return Modlist{}, err } err = json.Unmarshal(data, &mods) if err != nil { return Modlist{}, err } return mods, nil } // AddModsNotInList finds mod archives in the mod folder that aren't in the modlist, and adds them. func AddModsNotInList(modsdir string, mods *Modlist) error { r := regexp.MustCompile(fileRegex) files, err := os.ReadDir(modsdir) if err != nil { return err } for _, file := range files { if strings.HasSuffix(file.Name(), ".zip") && !file.IsDir() { groups := r.FindAllStringSubmatch(file.Name(), -1) name := groups[0][1] version := groups[0][2] if !mods.HasMod(name) { log.Printf("%s isn't enabled, adding v%s in disabled state.", name, version) mods.Mods = append(mods.Mods, Mod{ Name: name, Enabled: false, }) } } } return nil }