-
Notifications
You must be signed in to change notification settings - Fork 7
/
Cache.go
132 lines (115 loc) · 3.4 KB
/
Cache.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
package movie
import (
"bytes"
"io/ioutil"
"github.com/inkyblackness/hacked/ss1/content/audio"
"github.com/inkyblackness/hacked/ss1/content/text"
"github.com/inkyblackness/hacked/ss1/resource"
)
const sizeLimit = 0xFFFFFF
// Cache retrieves movie container from a localizer and keeps them decoded until they are invalidated.
type Cache struct {
cp text.Codepage
localizer resource.Localizer
movies map[resource.Key]*cachedMovie
}
type cachedMovie struct {
sizeWarning bool
container Container
scenes []Scene
}
func (cached *cachedMovie) video() []Scene {
if len(cached.scenes) > 0 {
return cached.scenes
}
cached.scenes, _ = cached.container.Video.Decompress()
return cached.scenes
}
// NewCache returns a new instance.
func NewCache(cp text.Codepage, localizer resource.Localizer) *Cache {
cache := &Cache{
cp: cp,
localizer: localizer,
movies: make(map[resource.Key]*cachedMovie),
}
return cache
}
// InvalidateResources lets the cache remove any movies from resources that are specified in the given slice.
func (cache *Cache) InvalidateResources(ids []resource.ID) {
for _, id := range ids {
for key := range cache.movies {
if key.ID == id {
delete(cache.movies, key)
}
}
}
}
func (cache *Cache) cached(key resource.Key) (*cachedMovie, error) {
value, existing := cache.movies[key]
if existing {
return value, nil
}
selector := cache.localizer.LocalizedResources(key.Lang)
view, err := selector.Select(key.ID.Plus(key.Index))
if err != nil {
return nil, err
}
if (view.ContentType() != resource.Movie) || view.Compound() || (view.BlockCount() != 1) {
return nil, resource.ErrWrongType(key, resource.Movie)
}
reader, err := view.Block(0)
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
container, err := Read(bytes.NewReader(data), cache.cp)
if err != nil {
return nil, err
}
cached := &cachedMovie{
sizeWarning: len(data) > sizeLimit,
container: container,
}
cache.movies[key] = cached
return cached, nil
}
// SizeWarning returns true if the given movie is cached and is larger than the underlying archive would support.
func (cache *Cache) SizeWarning(key resource.Key) bool {
cached, existing := cache.movies[key]
return existing && cached.sizeWarning
}
// Container retrieves and caches the underlying movie, and returns the complete container.
func (cache *Cache) Container(key resource.Key) (Container, error) {
cached, err := cache.cached(key)
if err != nil {
return Container{}, err
}
return cached.container, nil
}
// Audio retrieves and caches the underlying movie, and returns the audio only.
func (cache *Cache) Audio(key resource.Key) (sound audio.L8, err error) {
cached, err := cache.cached(key)
if err != nil {
return
}
return cached.container.Audio.Sound, nil
}
// Video retrieves and caches the underlying movie, and returns the complete list of decoded scenes.
func (cache *Cache) Video(key resource.Key) ([]Scene, error) {
cached, err := cache.cached(key)
if err != nil {
return nil, err
}
return cached.video(), nil
}
// Subtitles retrieves and caches the underlying movie, and returns the subtitles for given language.
func (cache *Cache) Subtitles(key resource.Key, language resource.Language) (SubtitleList, error) {
cached, err := cache.cached(key)
if err != nil {
return SubtitleList{}, err
}
return cached.container.Subtitles.PerLanguage[language], nil
}