doodle/pkg/userdir/userdir.go
Noah Petherbridge 35a89e5dbe WASM: Store User Files in localStorage
* In WASM build, user levels and doodads are written to localStorage
  using their userdir path as keys (".config/levels/test.level")
* LoadFile() and WriteFile() for both Levels and Doodads interact with
  the localStorage for WASM build instead of filesystem for desktop.
* userdir.ListLevels() and ListDoodads() for WASM scan the localStorage
  keys for file names.
* userdir.ResolvePath() now works for WASM (previously was dummied out),
  checks for the file in localStorage.
2019-06-27 15:59:18 -07:00

183 lines
4.6 KiB
Go

package userdir
import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"git.kirsle.net/apps/doodle/pkg/wasm"
"github.com/kirsle/configdir"
)
// Profile Directory settings.
var (
ConfigDirectoryName = "doodle"
ProfileDirectory string
LevelDirectory string
DoodadDirectory string
CacheDirectory string
FontDirectory string
)
// File extensions
const (
extLevel = ".level"
extDoodad = ".doodad"
)
func init() {
// Profile directory contains the user's levels and doodads.
ProfileDirectory = configdir.LocalConfig(ConfigDirectoryName)
LevelDirectory = configdir.LocalConfig(ConfigDirectoryName, "levels")
DoodadDirectory = configdir.LocalConfig(ConfigDirectoryName, "doodads")
// Cache directory to extract font files to.
CacheDirectory = configdir.LocalCache(ConfigDirectoryName)
FontDirectory = configdir.LocalCache(ConfigDirectoryName, "fonts")
// Ensure all the directories exist.
// WASM: do not make paths in wasm.
if runtime.GOOS != "js" {
configdir.MakePath(LevelDirectory)
configdir.MakePath(DoodadDirectory)
configdir.MakePath(FontDirectory)
}
}
// LevelPath will turn a "simple" filename into an absolute path in the user's
// local levels folder. If the filename already contains slashes, it is returned
// as-is as an absolute or relative path.
func LevelPath(filename string) string {
return resolvePath(LevelDirectory, filename, extLevel)
}
// DoodadPath is like LevelPath but for Doodad files.
func DoodadPath(filename string) string {
return resolvePath(DoodadDirectory, filename, extDoodad)
}
// CacheFilename returns a path to a file in the cache folder. Send in path
// components and not literal slashes, like
// CacheFilename("images", "chunks", "id.bmp")
func CacheFilename(filename ...string) string {
paths := append([]string{CacheDirectory}, filename...)
dir := paths[:len(paths)-1]
if runtime.GOOS != "js" {
configdir.MakePath(filepath.Join(dir...))
}
return filepath.Join(paths[0], filepath.Join(paths[1:]...))
}
// ListDoodads returns a listing of all available doodads.
func ListDoodads() ([]string, error) {
var names []string
// WASM: list from localStorage.
if runtime.GOOS == "js" {
return wasm.StorageKeys(DoodadDirectory + "/"), nil
}
files, err := ioutil.ReadDir(DoodadDirectory)
if err != nil {
return names, err
}
for _, file := range files {
name := file.Name()
if strings.HasSuffix(strings.ToLower(name), extDoodad) {
names = append(names, name)
}
}
return names, nil
}
// ListLevels returns a listing of all available levels.
func ListLevels() ([]string, error) {
var names []string
// WASM: list from localStorage.
if runtime.GOOS == "js" {
return wasm.StorageKeys(LevelDirectory + "/"), nil
}
files, err := ioutil.ReadDir(LevelDirectory)
if err != nil {
return names, err
}
for _, file := range files {
name := file.Name()
if strings.HasSuffix(strings.ToLower(name), extLevel) {
names = append(names, name)
}
}
return names, nil
}
// resolvePath is the inner logic for LevelPath and DoodadPath.
func resolvePath(directory, filename, extension string) string {
if strings.Contains(filename, "/") {
return filename
}
// Attach the file extension?
if strings.ToLower(filepath.Ext(filename)) != extension {
filename += extension
}
return filepath.Join(directory, filename)
}
// ResolvePath takes an ambiguous simple filename and searches for a Level or
// Doodad that matches. Returns a blank string if no files found.
//
// Pass a true value for `one` if you are intending to create the file. It will
// only test one filepath and return the first one, regardless if the file
// existed. So the filename should have a ".level" or ".doodad" extension and
// then this path will resolve the ProfileDirectory of the file.
func ResolvePath(filename, extension string, one bool) string {
// If the filename exists outright, return it.
if !(runtime.GOOS == "js") {
if _, err := os.Stat(filename); !os.IsNotExist(err) {
return filename
}
}
var paths []string
if extension == extLevel {
paths = append(paths, filepath.Join(LevelDirectory, filename))
} else if extension == extDoodad {
paths = append(paths, filepath.Join(DoodadDirectory, filename))
} else {
paths = append(paths,
filepath.Join(LevelDirectory, filename+".level"),
filepath.Join(DoodadDirectory, filename+".doodad"),
)
}
for _, test := range paths {
// WASM: check the path in localStorage.
if runtime.GOOS == "js" {
if _, ok := wasm.GetSession(test); ok {
return test
}
continue
}
// Desktop: test the filesystem.
if _, err := os.Stat(test); os.IsNotExist(err) {
continue
}
return test
}
return ""
}