Skip to content

Commit

Permalink
feat(config): Add support for per-game config overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
gabe565 committed Oct 24, 2024
1 parent fa8e63d commit 8af9040
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 59 deletions.
18 changes: 12 additions & 6 deletions cmd/gones/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,25 @@ func New(opts ...options.Option) *cobra.Command {
}

func runCobra(cmd *cobra.Command, args []string) error {
conf, err := config.Load(cmd)
if err != nil {
return err
}
cmd.SilenceUsage = true

var path string
if len(args) > 0 {
path = args[0]
}
cmd.SilenceUsage = true

cart, err := loadCartridge(path)
if err != nil {
return err
}

conf := config.NewDefault()
if err := conf.Load(cmd, cart.Name(), cart.Hash()); err != nil {
return err
}

ctx, cancel := signal.NotifyContext(cmd.Context(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
defer cancel()

return run(ctx, conf, path)
return run(ctx, conf, cart)
}
11 changes: 7 additions & 4 deletions cmd/gones/cmd_js.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package gones

import (
"log/slog"

"gabe565.com/gones/cmd/options"
"gabe565.com/gones/internal/config"
)

type Command struct{}

func (c *Command) Execute() error {
slog.Info("Loaded config")
conf := config.NewDefault()
return run(nil, conf, "")

cart, err := loadCartridge()
if err != nil {
return err
}

return run(nil, conf, cart)
}

func New(_ ...options.Option) *Command {
Expand Down
11 changes: 7 additions & 4 deletions cmd/gones/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,17 @@ import (
"github.com/ncruces/zenity"
)

func newConsole(conf *config.Config, path string) (*console.Console, error) {
func loadCartridge(path string) (*cartridge.Cartridge, error) {
if path == "" {
var err error
path, err = zenity.SelectFile(
if path, err = zenity.SelectFile(
zenity.Title("Choose a ROM file"),
zenity.FileFilter{
Name: "NES ROM",
Patterns: []string{"*.nes"},
CaseFold: true,
},
)
if err != nil {
); err != nil {
return nil, err
}
}
Expand All @@ -33,5 +32,9 @@ func newConsole(conf *config.Config, path string) (*console.Console, error) {
}
slog.Info("Loaded cartridge", "", cart)

return cart, nil
}

func newConsole(conf *config.Config, cart *cartridge.Cartridge) (*console.Console, error) {
return console.New(conf, cart)
}
6 changes: 5 additions & 1 deletion cmd/gones/console_js.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)

func newConsole(conf *config.Config, _ string) (*console.Console, error) {
func loadCartridge() (*cartridge.Cartridge, error) {
jsCartridge := js.Global().Get("GonesCartridge")
jsData := jsCartridge.Get("data")
goData := make([]byte, jsData.Get("length").Int())
Expand All @@ -29,6 +29,10 @@ func newConsole(conf *config.Config, _ string) (*console.Console, error) {
}
slog.Info("Loaded cartridge", "", cart)

return cart, nil
}

func newConsole(conf *config.Config, cart *cartridge.Cartridge) (*console.Console, error) {
js.Global().Get("GonesClient").Call("setRomName", cart.Name())

c, err := console.New(conf, cart)
Expand Down
5 changes: 3 additions & 2 deletions cmd/gones/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"log/slog"
"runtime"

"gabe565.com/gones/internal/cartridge"
"gabe565.com/gones/internal/config"
"gabe565.com/gones/internal/console"
"gabe565.com/gones/internal/pprof"
"github.com/hajimehoshi/ebiten/v2"
)

func run(ctx context.Context, conf *config.Config, path string) error {
func run(ctx context.Context, conf *config.Config, cart *cartridge.Cartridge) error {
if pprof.Enabled {
go func() {
if err := pprof.ListenAndServe(); err != nil {
Expand All @@ -21,7 +22,7 @@ func run(ctx context.Context, conf *config.Config, path string) error {
}()
}

c, err := newConsole(conf, path)
c, err := newConsole(conf, cart)
if err != nil {
return err
}
Expand Down
137 changes: 95 additions & 42 deletions internal/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"gabe565.com/gones/internal/consts"
"gabe565.com/gones/internal/log"
"gabe565.com/utils/must"
"github.com/knadh/koanf/providers/posflag"
"github.com/knadh/koanf/providers/rawbytes"
"github.com/knadh/koanf/providers/structs"
Expand All @@ -20,107 +21,159 @@ import (
"github.com/spf13/cobra"
)

func Load(cmd *cobra.Command) (*Config, error) {
func (conf *Config) Load(cmd *cobra.Command, name, hash string) error {
log.Init(cmd.ErrOrStderr())

k := koanf.New(".")
conf := NewDefault()

// Load default config
if err := k.Load(structs.Provider(conf, "toml"), nil); err != nil {
return nil, err
return err
}

// Find config file
cfgFile, err := cmd.Flags().GetString("config")
if err != nil {
return nil, err
}
var gameCfgFile string
cfgFile := must.Must2(cmd.Flags().GetString("config"))
if cfgFile == "" {
cfgDir, err := GetDir()
if err != nil {
return nil, err
return err
}

cfgFile = filepath.Join(cfgDir, "config.toml")
gameCfgFile = filepath.Join(cfgDir, "games", hash+".toml")
}

if err := conf.loadMainConfig(k, cfgFile); err != nil {
return err
}

if gameCfgFile != "" {
if err := conf.loadGameOverrides(k, gameCfgFile, name); err != nil {
return err
}
}

if err := conf.loadFlags(k, cmd); err != nil {
return err
}
logger := slog.With("file", cfgFile)

paletteDir, err := GetPaletteDir()
if err != nil {
return err
}

if err := os.MkdirAll(paletteDir, 0o777); err != nil {
return err
}

return err
}

func (conf *Config) loadMainConfig(k *koanf.Koanf, path string) error {
logger := slog.With("file", path)

var cfgNotExists bool
// Load config file if exists
cfgContents, err := os.ReadFile(cfgFile)
cfgContents, err := os.ReadFile(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
cfgNotExists = true
} else {
return nil, err
return err
}
}

// Parse config file
parser := TOMLParser{}
if err := k.Load(rawbytes.Provider(cfgContents), parser); err != nil {
return nil, err
if err := k.Load(rawbytes.Provider(cfgContents), TOMLParser{}); err != nil {
return err
}

if err := fixConfig(k); err != nil {
return nil, err
return err
}

if err := k.UnmarshalWithConf("", conf, koanf.UnmarshalConf{Tag: "toml"}); err != nil {
return nil, err
return err
}

// Update config if necessary
newCfg, err := toml.Marshal(conf)
if err != nil {
return nil, err
return err
}

if !bytes.Equal(cfgContents, newCfg) {
if cfgNotExists {
logger.Info("Creating config")
logger.Info("Creating main config")

if err := os.MkdirAll(filepath.Dir(cfgFile), 0o777); err != nil {
return nil, err
if err := os.MkdirAll(filepath.Dir(path), 0o777); err != nil {
return err
}
} else {
logger.Info("Updating config")
logger.Info("Updating main config")
}

if err := os.WriteFile(cfgFile, newCfg, 0o666); err != nil {
return nil, err
if err := os.WriteFile(path, newCfg, 0o666); err != nil {
return err
}
}

// Load flags
flagTable := flagTable()
err = k.Load(posflag.ProviderWithValue(cmd.Flags(), ".", k, func(key string, value string) (string, any) {
if k, ok := flagTable[key]; ok {
key = k
} else {
key = ""
}
return key, value
}), nil)
logger.Info("Loaded main config")
return nil
}

func (conf *Config) loadGameOverrides(k *koanf.Koanf, path, name string) error {
logger := slog.With("file", path)

b, err := os.ReadFile(path)
if err != nil {
return nil, err
if !os.IsNotExist(err) {
return err
}

logger.Info("Creating game config")
if err := os.MkdirAll(filepath.Dir(path), 0o777); err != nil {
return err
}

if err := os.WriteFile(path, []byte("# Config for "+name), 0o666); err != nil {
return err
}

return nil
}

if err := k.Load(rawbytes.Provider(b), TOMLParser{}); err != nil {
return err
}

if err := k.UnmarshalWithConf("", conf, koanf.UnmarshalConf{Tag: "toml"}); err != nil {
return nil, err
return err
}

paletteDir, err := GetPaletteDir()
if err != nil {
return nil, err
if err := fixConfig(k); err != nil {
return err
}

if err := os.MkdirAll(paletteDir, 0o777); err != nil && !errors.Is(err, os.ErrExist) {
return nil, err
logger.Info("Loaded game config")
return nil
}

func (conf *Config) loadFlags(k *koanf.Koanf, cmd *cobra.Command) error {
lookup := flagTable()
if err := k.Load(posflag.ProviderWithValue(cmd.Flags(), ".", k, func(key string, value string) (string, any) {
if k, ok := lookup[key]; ok {
key = k
} else {
key = ""
}
return key, value
}), nil); err != nil {
return err
}

logger.Info("Loaded config")
return conf, err
return k.UnmarshalWithConf("", conf, koanf.UnmarshalConf{Tag: "toml"})
}

func fixConfig(k *koanf.Koanf) error {
Expand Down

0 comments on commit 8af9040

Please sign in to comment.