diff --git a/.github/workflows/cd/cloudretro.io/config.yaml b/.github/workflows/cd/cloudretro.io/config.yaml index 180a570eb..ac29d6361 100644 --- a/.github/workflows/cd/cloudretro.io/config.yaml +++ b/.github/workflows/cd/cloudretro.io/config.yaml @@ -29,12 +29,12 @@ emulator: logLevel: 1 cores: list: + dos: + uniqueSaveDir: true mame: options: "fbneo-diagnostic-input": "Hold Start" nes: scale: 2 - pcsx: - altRepo: true snes: scale: 2 diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml index 2bab6fc25..62d7f8cd3 100644 --- a/pkg/config/config.yaml +++ b/pkg/config/config.yaml @@ -209,6 +209,8 @@ emulator: # - skip_hw_context_destroy -- don't destroy OpenGL context during Libretro core deinit. # May help with crashes, for example, with PPSSPP. # - 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. list: gba: lib: mgba_libretro diff --git a/pkg/config/emulator.go b/pkg/config/emulator.go index 2a25f7409..4db9b825e 100644 --- a/pkg/config/emulator.go +++ b/pkg/config/emulator.go @@ -55,6 +55,7 @@ type LibretroCoreConfig struct { Options4rom map[string]map[string]string // <(^_^)> Roms []string Scale float64 + UniqueSaveDir bool UsesLibCo bool VFR bool Width int diff --git a/pkg/os/os.go b/pkg/os/os.go index 142ffcc1c..c3ab27181 100644 --- a/pkg/os/os.go +++ b/pkg/os/os.go @@ -84,3 +84,7 @@ func StatSize(path string) (int64, error) { } return fi.Size(), nil } + +func RemoveAll(path string) error { + return os.RemoveAll(path) +} diff --git a/pkg/worker/caged/libretro/frontend.go b/pkg/worker/caged/libretro/frontend.go index a8465488c..60792d70e 100644 --- a/pkg/worker/caged/libretro/frontend.go +++ b/pkg/worker/caged/libretro/frontend.go @@ -66,6 +66,7 @@ type Frontend struct { DisableCanvasPool bool SaveOnClose bool + UniqueSaveDir bool } type Device byte @@ -153,6 +154,11 @@ func (f *Frontend) LoadCore(emu string) { KbMouseSupport: conf.KbMouseSupport, } f.mu.Lock() + if conf.UniqueSaveDir { + f.UniqueSaveDir = true + f.nano.SetSaveDirSuffix(f.storage.MainPath()) + f.log.Debug().Msgf("Using unique dir for saves: %v", f.storage.MainPath()) + } scale := 1.0 if conf.Scale > 1 { scale = conf.Scale @@ -336,6 +342,13 @@ func (f *Frontend) Close() { f.mui.Lock() f.nano.Close() + + if f.UniqueSaveDir && !f.HasSave() { + if err := f.nano.DeleteSaveDir(); err != nil { + f.log.Error().Msgf("couldn't delete save dir: %v", err) + } + } + f.mui.Unlock() f.log.Debug().Msgf("frontend closed") } diff --git a/pkg/worker/caged/libretro/nanoarch/nanoarch.go b/pkg/worker/caged/libretro/nanoarch/nanoarch.go index f334c64eb..16eec60f9 100644 --- a/pkg/worker/caged/libretro/nanoarch/nanoarch.go +++ b/pkg/worker/caged/libretro/nanoarch/nanoarch.go @@ -157,6 +157,22 @@ 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) SetSaveDirSuffix(sx string) { + if n.cSaveDirectory != nil { + C.free(unsafe.Pointer(n.cSaveDirectory)) + } + dir := C.GoString(n.cSaveDirectory) + "/" + sx + _ = os.CheckCreateDir(dir) + n.cSaveDirectory = C.CString(dir) +} +func (n *Nanoarch) DeleteSaveDir() error { + if n.cSaveDirectory == nil { + return nil + } + + dir := C.GoString(n.cSaveDirectory) + return os.RemoveAll(dir) +} func (n *Nanoarch) CoreLoad(meta Metadata) { var err error diff --git a/pkg/worker/caged/libretro/storage.go b/pkg/worker/caged/libretro/storage.go index fc1a5a76c..fc58faaf6 100644 --- a/pkg/worker/caged/libretro/storage.go +++ b/pkg/worker/caged/libretro/storage.go @@ -10,6 +10,7 @@ import ( type ( Storage interface { + MainPath() string GetSavePath() string GetSRAMPath() string SetMainSaveName(name string) @@ -32,6 +33,7 @@ type ( } ) +func (s *StateStorage) MainPath() string { return s.MainSave } func (s *StateStorage) SetMainSaveName(name string) { s.MainSave = name } func (s *StateStorage) SetNonBlocking(v bool) { s.NonBlock = v } func (s *StateStorage) GetSavePath() string { return filepath.Join(s.Path, s.MainSave+".dat") }