fml/internal/modlist/modlist.go

181 lines
3.9 KiB
Go

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 {
mod := &modlist.Mods[i]
if mod.Name == name && mod.Enabled != status {
mod.Enabled = status
log.Debugf("Setting status of mod %s to %v", mod.Name, mod.Enabled)
}
}
}
func (modlist *Modlist) setAllStatus(status bool) {
for i := range modlist.Mods {
mod := &modlist.Mods[i]
mod.Enabled = status
log.Debugf("Setting status of mod %s to %v", mod.Name, mod.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))
log.Infof("Wrote modlist to file %s.", filename)
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
}
log.Infof("Read modlist from file %s.", filename)
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.Info("Found %s not in modlist, adding v%s in disabled state.", name, version)
mods.Mods = append(mods.Mods, Mod{
Name: name,
Enabled: false,
})
}
}
}
return nil
}