Skip to content

Commit

Permalink
Add new saveStateFs config param
Browse files Browse the repository at this point in the history
Used when you need a copy of FS for new game sessions (i.e. DOSBox uniqueSaveDir=true).
  • Loading branch information
sergystepanov committed Nov 26, 2024
1 parent 71f5de3 commit 1831e44
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 15 deletions.
5 changes: 5 additions & 0 deletions pkg/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ library:
ignored:
- neogeo
- pgm
# DOSBox filesystem state
- .pure
# an explicit list of supported file extensions
# which overrides Libretro emulator ROMs configs
supported:
Expand Down Expand Up @@ -216,6 +218,8 @@ emulator:
# - skip_same_thread_save -- skip thread lock save (used with PPSSPP).
# - uniqueSaveDir (bool) -- needed only for cores (like DosBox) that persist their state into one shared file.
# This will allow for concurrent reading and saving of current states.
# - saveStateFs (string) -- the name of the file that will be initially copied into the save folder.
# All * symbols will be replaced to the name of the ROM.
list:
gba:
lib: mgba_libretro
Expand Down Expand Up @@ -281,6 +285,7 @@ emulator:
folder: dos
kbMouseSupport: true
nonBlockingSave: true
saveStateFs: "*.pure.zip"
hid:
0: [ 257, 513 ]
1: [ 257, 513 ]
Expand Down
1 change: 1 addition & 0 deletions pkg/config/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type LibretroCoreConfig struct {
Options map[string]string
Options4rom map[string]map[string]string // <(^_^)>
Roms []string
SaveStateFs string
Scale float64
UniqueSaveDir bool
UsesLibCo bool
Expand Down
42 changes: 28 additions & 14 deletions pkg/games/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type libConf struct {
aliasFile string
path string
supported map[string]struct{}
ignored map[string]struct{}
ignored []string
verbose bool
watchMode bool
sessionPath string
Expand Down Expand Up @@ -98,7 +98,7 @@ func NewLib(conf config.Library, emu WithEmulatorInfo, log *logger.Logger) GameL
aliasFile: conf.AliasFile,
path: dir,
supported: toMap(conf.Supported),
ignored: toMap(conf.Ignored),
ignored: conf.Ignored,
verbose: conf.Verbose,
watchMode: conf.WatchMode,
sessionPath: emu.SessionStoragePath(),
Expand Down Expand Up @@ -207,22 +207,36 @@ func (lib *library) Scan() {
return err
}

if info != nil && !info.IsDir() && lib.isExtAllowed(path) {
meta := getMetadata(path, dir)
if info == nil || info.IsDir() || !lib.isExtAllowed(path) {
return nil
}

meta.System = lib.emuConf.GetEmulator(meta.Type, meta.Path)
meta := metadata(path, dir)
meta.System = lib.emuConf.GetEmulator(meta.Type, meta.Path)

if aliases != nil {
k, ok := aliases[meta.Name]
if ok {
meta.Alias = k
}
if aliases != nil {
if k, ok := aliases[meta.Name]; ok {
meta.Alias = k
}
}

ignored := false
for _, k := range lib.config.ignored {
if meta.Name == k {
ignored = true
break
}

if _, ok := lib.config.ignored[meta.Name]; !ok {
games = append(games, meta)
if len(k) > 0 && k[0] == '.' && strings.Contains(meta.Name, k) {
ignored = true
break
}
}

if !ignored {
games = append(games, meta)
}

return nil
})

Expand Down Expand Up @@ -322,8 +336,8 @@ func (lib *library) isExtAllowed(path string) bool {
return ok
}

// getMetadata returns game info from a path
func getMetadata(path string, basePath string) GameMetadata {
// metadata returns game info from a path
func metadata(path string, basePath string) GameMetadata {
name := filepath.Base(path)
ext := filepath.Ext(name)
relPath, _ := filepath.Rel(basePath, path)
Expand Down
12 changes: 12 additions & 0 deletions pkg/os/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ func GetUserHome() (string, error) {
return me.HomeDir, nil
}

func CopyFile(from string, to string) error {
bytesRead, err := os.ReadFile(from)
if err != nil {
return err
}
err = os.WriteFile(to, bytesRead, 0755)
if err != nil {
return err
}
return nil
}

func WriteFile(name string, data []byte, perm os.FileMode) error {
return os.WriteFile(name, data, perm)
}
Expand Down
43 changes: 42 additions & 1 deletion pkg/worker/caged/libretro/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"path/filepath"
"strings"
"sync"
"time"
"unsafe"
Expand Down Expand Up @@ -67,6 +68,7 @@ type Frontend struct {
DisableCanvasPool bool
SaveOnClose bool
UniqueSaveDir bool
SaveStateFs string
}

type Device byte
Expand Down Expand Up @@ -154,6 +156,7 @@ func (f *Frontend) LoadCore(emu string) {
KbMouseSupport: conf.KbMouseSupport,
}
f.mu.Lock()
f.SaveStateFs = conf.SaveStateFs
if conf.UniqueSaveDir {
f.UniqueSaveDir = true
f.nano.SetSaveDirSuffix(f.storage.MainPath())
Expand Down Expand Up @@ -287,6 +290,13 @@ func (f *Frontend) Start() {
}
}

func (f *Frontend) LoadGame(path string) error {
if f.UniqueSaveDir {
f.copyFsMaybe(path)
}
return f.nano.LoadGame(path)
}

func (f *Frontend) AspectRatio() float32 { return f.nano.AspectRatio() }
func (f *Frontend) AudioSampleRate() int { return f.nano.AudioSampleRate() }
func (f *Frontend) FPS() int { return f.nano.VideoFramerate() }
Expand All @@ -296,7 +306,6 @@ func (f *Frontend) HasSave() bool { return os.Exists(f.HashPath(
func (f *Frontend) HashPath() string { return f.storage.GetSavePath() }
func (f *Frontend) IsPortrait() bool { return f.nano.IsPortrait() }
func (f *Frontend) KbMouseSupport() bool { return f.nano.KbMouseSupport() }
func (f *Frontend) LoadGame(path string) error { return f.nano.LoadGame(path) }
func (f *Frontend) PixFormat() uint32 { return f.nano.Video.PixFmt.C }
func (f *Frontend) RestoreGameState() error { return f.Load() }
func (f *Frontend) Rotation() uint { return f.nano.Rot }
Expand Down Expand Up @@ -349,6 +358,9 @@ func (f *Frontend) Close() {
}
}

f.UniqueSaveDir = false
f.SaveStateFs = ""

f.mui.Unlock()
f.log.Debug().Msgf("frontend closed")
}
Expand Down Expand Up @@ -420,3 +432,32 @@ func (f *Frontend) autosave(periodSec int) {
}
}
}

func (f *Frontend) copyFsMaybe(path string) {
if f.SaveStateFs == "" {
return
}

fileName := f.SaveStateFs
hasPlaceholder := strings.HasPrefix(f.SaveStateFs, "*")
if hasPlaceholder {
game := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
fileName = strings.Replace(f.SaveStateFs, "*", game, 1)
}

fullPath := filepath.Join(f.nano.SaveDir(), fileName)

if os.Exists(fullPath) {
return
}

storePath := filepath.Dir(path)
fsPath := filepath.Join(storePath, fileName)
if os.Exists(fsPath) {
if err := os.CopyFile(fsPath, fullPath); err != nil {
f.log.Error().Err(err).Msgf("fs copy fail")
} else {
f.log.Debug().Msgf("copied fs %v to %v", fsPath, fullPath)
}
}
}
1 change: 1 addition & 0 deletions pkg/worker/caged/libretro/nanoarch/nanoarch.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func (n *Nanoarch) WaitReady() { <-n.reserved }
func (n *Nanoarch) Close() { n.Stopped.Store(true); n.reserved <- struct{}{} }
func (n *Nanoarch) SetLogger(log *logger.Logger) { n.log = log }
func (n *Nanoarch) SetVideoDebounce(t time.Duration) { n.limiter = NewLimit(t) }
func (n *Nanoarch) SaveDir() string { return C.GoString(n.cSaveDirectory) }
func (n *Nanoarch) SetSaveDirSuffix(sx string) {
dir := C.GoString(n.cSaveDirectory) + "/" + sx
err := os.CheckCreateDir(dir)
Expand Down

0 comments on commit 1831e44

Please sign in to comment.