forked from ZaaLabs/PushButtonEngine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ResourceManager.as
274 lines (245 loc) · 12 KB
/
ResourceManager.as
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/*******************************************************************************
* PushButton Engine
* Copyright (C) 2009 PushButton Labs, LLC
* For more information see http://www.pushbuttonengine.com
*
* This file is licensed under the terms of the MIT license, which is included
* in the License.html file at the root directory of this SDK.
******************************************************************************/
package com.pblabs.engine.resource
{
import com.pblabs.engine.PBE;
import com.pblabs.engine.PBUtil;
import com.pblabs.engine.debug.Logger;
import com.pblabs.engine.ns_pbe;
import com.pblabs.engine.resource.provider.EmbeddedResourceProvider;
import com.pblabs.engine.resource.provider.FallbackResourceProvider;
import com.pblabs.engine.resource.provider.IResourceProvider;
import com.pblabs.engine.serialization.TypeUtility;
import flash.events.Event;
import flash.utils.Dictionary;
/**
* The resource manager handles all tasks related to using asset files (images, xml, etc)
* in a project. This includes loading files, managing embedded resources, and cleaninp up
* resources no longer in use.
*/
public class ResourceManager
{
/**
* If true, we will never get resources from outside the SWF - only resources
* that have been properly embedded and registered with the ResourceManager
* will be used.
*/
public var onlyLoadEmbeddedResources:Boolean = false;
/**
* Function that will be called if OnlyLoadEmbeddedResources is set and we
* fail to find something. Useful for displaying feedback for artists (such
* as via a dialog box ;). Passed the filename of the requested resource.
*/
public var onEmbeddedFail:Function;
/**
* Loads a resource from a file. If the resource has already been loaded or is embedded, a
* reference to the existing resource will be given. The resource is not returned directly
* since loading is asynchronous. Instead, it will be passed to the function specified in
* the onLoaded parameter. Even if the resource has already been loaded, it cannot be
* assumed that the callback will happen synchronously.
*
* <p>This will not attempt to load resources that have previously failed to load. Instead,
* the load will fail instantly.</p>
*
* @param filename The url of the file to load.
* @param resourceType The Resource subclass specifying the type of resource that is being
* requested.
* @param onLoaded A function that will be called on successful load of the resource. The
* function should take a single parameter of the type specified in the resourceType
* parameter.
* @param onFailed A function that will be called if loading of the resource fails. The
* function should take a single parameter of the type specified in the resourceType
* parameter. The resource passed to the function will be invalid, but the filename
* property will be correct.
* @param forceReload Always reload the resource, even if it has already been loaded.
*
* @see Resource
*/
public function load(filename:String, resourceType:Class, onLoaded:Function = null, onFailed:Function = null, forceReload:Boolean = false):Resource
{
// Sanity!
if(filename == null || filename == "")
{
Logger.error(this, "load", "Cannot load a " + resourceType + " with empty filename.");
return null;
}
// Look up the resource.
var resourceIdentifier:String = filename.toLowerCase() + resourceType;
var resource:Resource = _resources[resourceIdentifier];
// If it was loaded and we want to force a reload, do that.
if (resource && forceReload)
{
_resources[resourceIdentifier] = null;
delete _resources[resourceIdentifier];
resource = null;
}
// If it wasn't loaded...
if (!resource)
{
// Then it wasn't embedded, so error if we're configured for that.
if(onlyLoadEmbeddedResources && !EmbeddedResourceProvider.instance.isResourceKnown(filename, resourceType))
{
var tmpR:Resource = new Resource();
tmpR.filename = filename;
tmpR.fail("not embedded in the SWF with type " + resourceType + ".");
fail(tmpR, onFailed, "'" + filename + "' was not loaded because it was not embedded in the SWF with type " + resourceType + ".");
if(onEmbeddedFail != null)
onEmbeddedFail(filename);
return null;
}
// Hack for MP3 and WAV files. TODO: Generalize this for arbitrary formats.
var fileExtension:String = PBUtil.getFileExtension(filename).toLocaleLowerCase();
if(resourceType == SoundResource && (fileExtension == "mp3" || fileExtension == "wav"))
resourceType = MP3Resource;
// check available resource providers and request the resource if it is known
for (var rp:int = 0; rp < resourceProviders.length; rp++)
{
if ((resourceProviders[rp] as IResourceProvider).isResourceKnown(filename, resourceType))
resource = (resourceProviders[rp] as IResourceProvider).getResource(filename, resourceType, forceReload);
}
// If we couldn't find a match, fall back to the default provider.
if (!resource)
resource = FallbackResourceProvider.instance.getResource(filename, resourceType, forceReload);
// Make sure the filename is set.
if(!resource.filename)
resource.filename = filename;
// Store it in the resource dictionary.
_resources[resourceIdentifier] = resource;
}
else if (!(resource is resourceType))
{
fail(resource, onFailed, "The resource " + filename + " is already loaded, but is of type " + TypeUtility.getObjectClassName(resource) + " rather than the specified " + resourceType + ".");
return null;
}
// Deal with it if it already failed, already loaded, or if it is still pending.
if (resource.didFail)
{
fail(resource, onFailed, "The resource " + filename + " has previously failed to load");
}
else if (resource.isLoaded)
{
if (onLoaded != null)
PBE.callLater(onLoaded, [ resource ]);
}
else
{
// Still in process, so just hook up to its events.
if (onLoaded != null)
resource.addEventListener(ResourceEvent.LOADED_EVENT, function (event:Event):void { onLoaded(resource); } );
if (onFailed != null)
resource.addEventListener(ResourceEvent.FAILED_EVENT, function (event:Event):void { onFailed(resource); } );
}
// Don't forget to bump its ref count.
resource.incrementReferenceCount();
return resource;
}
/**
* Unloads a previously loaded resource. This does not necessarily mean the resource
* will be available for garbage collection. Resources are reference counted so if
* the specified resource has been loaded multiple times, its reference count will
* only decrease as a result of this.
*
* @param filename The url of the resource to unload.
* @param resourceType The type of the resource to unload.
*/
public function unload(filename:String, resourceType:Class):void
{
var resourceIdentifier:String = filename.toLowerCase() + resourceType;
if (!_resources[resourceIdentifier])
{
Logger.warn(this, "Unload", "The resource from file " + filename + " of type " + resourceType + " is not loaded.");
return;
}
_resources[resourceIdentifier].decrementReferenceCount();
if (_resources[resourceIdentifier].referenceCount < 1)
{
_resources[resourceIdentifier] = null;
delete _resources[resourceIdentifier];
// unload with resourceProvider
for (var rp:int = 0; rp < resourceProviders.length; rp++)
{
if ((resourceProviders[rp] as IResourceProvider).isResourceKnown(filename, resourceType))
{
(resourceProviders[rp] as IResourceProvider).unloadResource(filename, resourceType);
return;
}
};
FallbackResourceProvider.instance.unloadResource(filename, resourceType);
}
}
/**
* Provide a source for resources to the ResourceManager. Once added,
* the ResourceManager will use this IResourceProvider to try to fulfill
* resource load requests.
*
* @param resourceProvider Provider to add.
* @see IResourceProvider
*/
public function registerResourceProvider(resourceProvider:IResourceProvider):void
{
// check if resourceProvider is already registered
if (resourceProviders.indexOf(resourceProvider) != -1)
{
Logger.warn(ResourceManager, "registerResourceProvider", "Tried to register ResourceProvider '" + resourceProvider + "' twice. Ignoring...");
return;
}
// add resourceProvider to list of known resourceProviders
resourceProviders.push(resourceProvider);
}
/**
* Check if a resource is loaded and ready to go.
* @param filename Same as request to load()
* @param type Same as request to load().
* @return True if resource is loaded.
*/
public function isLoaded(filename:String, resourceType:Class):Boolean
{
var resourceIdentifier:String = filename.toLowerCase() + resourceType;
if(!_resources[resourceIdentifier])
return false;
var r:Resource = _resources[resourceIdentifier];
return r.isLoaded;
}
/**
* Provides a resource if it is known. could be that it is not loaded yet.
* @param filename Same as request to load()
* @param type Same as request to load().
* @return resource
*/
public function getResource(filename:String, resourceType:Class):Resource
{
var resourceIdentifier:String = filename.toLowerCase() + resourceType;
return _resources[resourceIdentifier];
}
/**
* Properly mark a resource as failed-to-load.
*/
private function fail(resource:Resource, onFailed:Function, message:String):void
{
if(!resource)
throw new Error("Tried to fail null resource.");
Logger.error(this, "load", message);
if (onFailed != null)
PBE.callLater(onFailed, [resource]);
}
/**
* Dictionary of loaded resources indexed by resource name and type.
*/
private var _resources:Dictionary = new Dictionary();
/**
* List of resource providers used to get resources.
*/
private var resourceProviders:Array = new Array();
/*** Helper methods for PBE not externally exposed ***/
ns_pbe function getResources():Dictionary
{
return _resources;
}
}
}