File: ../src/resources/pack.js
///@INFO: PACK
//defined before Prefab
/**
* Pack is an object that contain several resources, helpful when you want to carry a whole scene in one single file
*
* @class Pack
* @constructor
*/
function Pack(o)
{
this.resource_names = [];
this.metadata = null;
this._data = {}; //the original chunks from the WBin, including the @JSON and @resource_names
this._resources_data = {}; //every resource in arraybuffer format
if(o)
this.configure(o);
}
Pack.version = "0.2"; //used to know where the file comes from
/**
* configure the pack from an unpacked WBin
* @method configure
* @param {Object} data an unpacked WBIN (object with every chunk)
**/
Pack.prototype.configure = function( data )
{
this._data = LS.cloneObject( data );
//extract resource names
this.resource_names = data["@resource_names"];
this._resources_data = {};
if(this.resource_names)
{
delete this._data["@resource_names"];
for(var i in this.resource_names)
{
this._resources_data[ this.resource_names[i] ] = data[ "@RES_" + i ];
delete this._data[ "@RES_" + i ];
}
}
//store resources in LS.ResourcesManager
this.processResources();
}
Object.defineProperty( Pack.prototype, 'bindata', {
set: function(name)
{
throw("Pack bindata cannot be assigned");
},
get: function(){
if(!this._original_data)
this._original_data = LS.Pack.packResources( this.resource_names, this._data );
return this._original_data;
},
enumerable: true
});
Pack.fromBinary = function(data)
{
if(data.constructor == ArrayBuffer)
data = WBin.load(data, true);
return new LS.Pack(data);
}
//given a list of resources that come from the Pack (usually a wbin) it extracts, process and register them
Pack.prototype.processResources = function()
{
if(!this.resource_names)
return;
var pack_filename = this.fullpath || this.filename;
//block this resources of being loaded, this is to avoid chain reactions when a resource uses
//another one contained in this pack
for(var i = 0; i < this.resource_names.length; ++i)
{
var resname = this.resource_names[i];
if( LS.ResourcesManager.resources[ resname ] )
continue; //already loaded
LS.ResourcesManager.resources_being_processed[ resname ] = true;
}
//process and store in LS.ResourcesManager
for(var i = 0; i < this.resource_names.length; ++i)
{
var resname = this.resource_names[i];
if( LS.ResourcesManager.resources[resname] )
continue; //already loaded
var resdata = this._resources_data[ resname ];
if(!resdata)
{
console.warn("resource data in Pack is undefined, skipping it:" + resname);
continue;
}
var resource = LS.ResourcesManager.processResource( resname, resdata, { is_local: true, from_pack: pack_filename } );
}
}
Pack.prototype.setResources = function( resource_names, mark_them )
{
this.resource_names = [];
this._resources_data = {};
var pack_filename = this.fullpath || this.filename;
//get resources
for(var i = 0; i < resource_names.length; ++i)
{
var res_name = resource_names[i];
if(this.resource_names.indexOf(res_name) != -1)
continue;
var resource = LS.ResourcesManager.resources[ res_name ];
if(!resource)
continue;
if(mark_them)
resource.from_pack = pack_filename;
this.resource_names.push( res_name );
}
//repack the pack info
this._original_data = LS.Pack.packResources( resource_names, this.getBaseData() );
this._modified = true;
}
Pack.prototype.getBaseData = function()
{
return { "@metadata": this.metadata, "@version": LS.Pack.version };
}
//adds to every resource in this pack info about where it came from (the pack)
Pack.prototype.setResourcesLink = function( value )
{
for(var i = 0; i < this.resource_names.length; ++i)
{
var res_name = this.resource_names[i];
var resource = LS.ResourcesManager.resources[ res_name ];
if(!resource)
continue;
if(value)
resource.from_pack = value;
else
delete resource.from_pack;
}
}
//adds a new resource (or array of resources) to this pack
Pack.prototype.addResources = function( resource_names, mark_them )
{
if(!resource_names)
return;
if(resource_names.constructor !== Array)
resource_names = [ resource_names ];
this.setResources( this.resource_names.concat( resource_names ), mark_them );
}
/**
* Adds a resource to the prefab
* @method addResource
* @param {String} filename filename of the resource
**/
Pack.prototype.addResource = function( filename )
{
filename = LS.ResourcesManager.cleanFullpath( filename );
var index = this.resource_names.indexOf(filename);
if(index == -1)
this.resource_names.push( filename );
}
/**
* Remove a resource to the prefab
* @method removeResource
* @param {String} filename filename of the resource
**/
Pack.prototype.removeResource = function(filename)
{
filename = LS.ResourcesManager.cleanFullpath( filename );
var index = this.resource_names.indexOf(filename);
if(index != -1)
this.resource_names.splice( index, 1 );
}
/**
* to create a WBin containing all the resource and metadata
* @method Pack.createWBin
* @param {String} fullpath for the pack
* @param {Array} resource_names array with the names of all the resources to store
* @param {Object} metadata [optional] extra data to store
* @param {boolean} mark_them [optional] marks all the resources as if they come from a pack
* @return object containing the pack data ready to be converted to WBin
**/
Pack.createPack = function( filename, resource_names, extra_data, mark_them )
{
if(!filename)
return;
if(!resource_names || resource_names.constructor !== Array)
throw("Pack.createPack resources must be array with names");
if(extra_data && extra_data.constructor !== Object)
throw("Pack.createPack extra_data must be an object with the chunks to store");
filename = filename.replace(/ /gi,"_");
var pack = new LS.Pack();
filename += ".wbin";
pack.filename = filename;
if(extra_data)
pack._data = extra_data;
pack.resource_names = resource_names;
for(var i = 0; i < resource_names.length; ++i)
{
var res_name = resource_names[i];
var resource = LS.ResourcesManager.resources[ res_name ];
if(!resource)
continue;
if(mark_them)
resource.from_pack = pack.filename;
}
//create the WBIN in case this pack gets stored
this.metadata = extra_data;
var bindata = LS.Pack.packResources( resource_names, pack.getBaseData() );
pack._original_data = bindata;
return pack;
}
//Given a bunch of resource names it creates a WBin with all inside
Pack.packResources = function( resource_names, base_object )
{
var to_binary = base_object || {};
var final_resource_names = [];
for(var i = 0; i < resource_names.length; ++i)
{
var res_name = resource_names[i];
var resource = LS.ResourcesManager.resources[ res_name ];
if(!resource)
continue;
var data = null;
if(resource._original_data) //must be string or bytes
data = resource._original_data;
else
{
var data_info = LS.Resource.getDataToStore( resource );
data = data_info.data;
}
if(!data)
{
console.warn("Wrong data in resource");
continue;
}
if(data.constructor === Blob || data.constructor === File)
{
if(!data.data || data.data.constructor !== ArrayBuffer )
{
console.warn("WBin does not support to store File or Blob, please, use ArrayBuffer");
continue;
}
data = data.data; //because files have an arraybuffer with the data if it was read
}
to_binary["@RES_" + final_resource_names.length ] = data;
final_resource_names.push( res_name );
//to_binary[res_name] = data;
}
to_binary["@resource_names"] = final_resource_names;
return WBin.create( to_binary, "Pack" );
}
//just tells the resources where they come from, we cannot do that before because we didnt have the name of the pack
Pack.prototype.flagResources = function()
{
if(!this.resource_names)
return;
for(var i = 0; i < this.resource_names.length; ++i)
{
var res_name = this.resource_names[i];
var resource = LS.ResourcesManager.resources[ res_name ];
if(!resource)
continue;
resource.from_pack = this.fullpath || this.filename;
}
}
Pack.prototype.getDataToStore = function()
{
return LS.Pack.packResources( this.resource_names, this.getBaseData() );
}
Pack.prototype.checkResourceNames = function()
{
if(!this.resource_names)
return 0;
var changed = 0;
for(var i = 0; i < this.resource_names.length; ++i)
{
var res_name = this.resource_names[i];
var old_name = res_name;
var resource = LS.ResourcesManager.resources[ res_name ];
if(!resource)
continue;
//avoid problematic symbols
if( LS.ResourcesManager.valid_resource_name_reg.test( res_name ) == false )
{
console.warn("Invalid filename in pack/prefab: ", res_name );
res_name = res_name.replace( /[^a-zA-Z0-9-_\.\/]/g, '_' );
}
//ensure extensions
var extension = LS.ResourcesManager.getExtension( res_name );
if(!extension)
{
extension = resource.constructor.EXTENSION;
if(!extension)
console.warn("Resource without extension and not known default extension: ", res_name , resource.constructor.name );
else
res_name = res_name + "." + extension;
}
if(old_name == res_name)
continue;
this.resource_names[i] = res_name;
LS.ResourcesManager.renameResource( old_name, res_name ); //force change
changed++;
}
if(changed)
LS.ResourcesManager.resourceModified( this );
return changed;
}
Pack.prototype.onResourceRenamed = function( old_name, new_name, resource )
{
if(!this.resource_names)
return;
var index = this.resource_names[ old_name ];
if( index == -1 )
return;
this.resource_names[ index ] = new_name;
LS.ResourcesManager.resourceModified( this );
}
Pack.prototype.containsResources = function()
{
return this.resource_names && this.resource_names.length > 0 ? true : false;
}
LS.Pack = Pack;
LS.registerResourceClass( Pack );