forked from EngoEngine/engo
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathassets.go
148 lines (128 loc) · 4.59 KB
/
assets.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package engo
import (
"fmt"
"io"
"os"
"path/filepath"
)
// FileLoader implements support for loading and releasing file resources.
type FileLoader interface {
// Load loads the given resource into memory.
Load(url string, data io.Reader) error
// Unload releases the given resource from memory.
Unload(url string) error
// Resource returns the given resource, and an error if it didn't succeed.
Resource(url string) (Resource, error)
}
// FileLoaderRooter must be implemented by file loaders that need to known the
// root for their own functionning. This is generaly because they need to
// load other files that are referenced inside the first one and are relative
// to the first one, so their own call to open() need to be aware of the root
// dir or the load will fail.
// This is typically the case of a tmx loader that needs to load .tsx files that
// are given as urls inside the tmx
type FileLoaderRooter interface {
// SetRoot gives the root to the file loader. Generally before calling
// the Load method
SetRoot(root string)
}
// Resource represents a game resource, such as an image or a sound.
type Resource interface {
// URL returns the uniform resource locator of the given resource.
URL() string
}
// Files manages global resource handling of registered file formats for game
// assets.
var Files = &Formats{formats: make(map[string]FileLoader)}
// Formats manages resource handling of registered file formats.
type Formats struct {
// formats maps from file extensions to resource loaders.
formats map[string]FileLoader
// root is the directory which is prepended to every resource url internally.
root string
}
// SetRoot can be used to change the default directory from `assets` to whatever you want.
//
// Whenever `root` does not start with the directory `assets`, you will not be able to support mobile (Android/iOS)
// since they require you to put all resources within the `assets` directory. More information about that is available
// here: https://godoc.org/golang.org/x/mobile/asset
//
// You can, however, use subfolders within the `assets` folder, and set those as `root`.
func (formats *Formats) SetRoot(root string) {
formats.root = root
}
// GetRoot returns the folder currently set at root.
func (formats *Formats) GetRoot() string {
return formats.root
}
// Register registers a resource loader for the given file format.
func (formats *Formats) Register(ext string, loader FileLoader) {
formats.formats[ext] = loader
}
// getExt returns the extension of the file(including extensions with `.` in them) from the given url.
func getExt(path string) string {
ext := ""
for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
if path[i] == '.' {
ext = path[i:]
}
}
return ext
}
// load loads the given resource into memory.
func (formats *Formats) load(url string) error {
ext := getExt(url)
if loader, ok := Files.formats[ext]; ok {
f, err := openFile(filepath.Join(formats.root, url))
if err != nil {
return fmt.Errorf("unable to open resource: %s", err)
}
defer f.Close()
// This specific loader needs to be given the root
rl, ok := loader.(FileLoaderRooter)
if ok {
rl.SetRoot(formats.GetRoot())
}
return loader.Load(url, f)
}
return fmt.Errorf("no `FileLoader` associated with this extension: %q in url %q", ext, url)
}
// Load loads the given resource(s) into memory, stopping at the first error.
func (formats *Formats) Load(urls ...string) error {
for _, url := range urls {
err := formats.load(url)
if err != nil {
return err
}
}
return nil
}
// LoadReaderData loads a resource when you already have the reader for it.
func (formats *Formats) LoadReaderData(url string, f io.Reader) error {
ext := getExt(url)
if loader, ok := Files.formats[ext]; ok {
// This specific loader needs to be given the root
rl, ok := loader.(FileLoaderRooter)
if ok {
rl.SetRoot(formats.GetRoot())
}
return loader.Load(url, f)
}
return fmt.Errorf("no `FileLoader` associated with this extension: %q in url %q", ext, url)
}
// Unload releases the given resource from memory.
func (formats *Formats) Unload(url string) error {
ext := getExt(url)
if loader, ok := Files.formats[ext]; ok {
return loader.Unload(url)
}
return fmt.Errorf("no `FileLoader` associated with this extension: %q in url %q", ext, url)
}
// Resource returns the given resource, and an error if it didn't succeed.
func (formats *Formats) Resource(url string) (Resource, error) {
ext := getExt(url)
if loader, ok := Files.formats[ext]; ok {
return loader.Resource(url)
}
return nil, fmt.Errorf("no `FileLoader` associated with this extension: %q in url %q", ext, url)
}