/*
* Copyright (c) 2007-2008, openmetaverse.org
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Neither the name of the openmetaverse.org nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Text;
using System.Collections.Generic;
using OpenMetaverse.Imaging;
namespace OpenMetaverse
{
///
///
///
[Flags]
public enum WearableType : byte
{
/// A shape
Shape = 0,
///
Skin,
///
Hair,
///
Eyes,
///
Shirt,
///
Pants,
///
Shoes,
///
Socks,
///
Jacket,
///
Gloves,
///
Undershirt,
///
Underpants,
///
Skirt,
///
Invalid = 255
};
///
/// Base class for all Asset types
///
public abstract class Asset
{
/// A byte array containing the raw asset data
public byte[] AssetData;
/// True if the asset it only stored on the server temporarily
public bool Temporary;
/// A unique ID
private UUID _AssetID;
/// The assets unique ID
public UUID AssetID
{
get { return _AssetID; }
internal set { _AssetID = value; }
}
///
/// The "type" of asset, Notecard, Animation, etc
///
public abstract AssetType AssetType
{
get;
}
///
/// Construct a new Asset object
///
public Asset() { }
///
/// Construct a new Asset object
///
/// A unique specific to this asset
/// A byte array containing the raw asset data
public Asset(UUID assetID, byte[] assetData)
{
_AssetID = assetID;
AssetData = assetData;
}
///
/// Regenerates the AssetData byte array from the properties
/// of the derived class.
///
public abstract void Encode();
///
/// Decodes the AssetData, placing it in appropriate properties of the derived
/// class.
///
/// True if the asset decoding succeeded, otherwise false
public abstract bool Decode();
}
///
/// Represents an Animation
///
public class AssetAnimation : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Animation; } }
/// Default Constructor
public AssetAnimation() { }
///
/// Construct an Asset object of type Animation
///
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetAnimation(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
AssetData = assetData;
}
public override void Encode() { }
public override bool Decode() { return true; }
}
///
/// Represents a string of characters encoded with specific formatting properties
///
public class AssetNotecard : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Notecard; } }
/// A text string containing the raw contents of the notecard
public string Text = null;
/// Construct an Asset of type Notecard
public AssetNotecard() { }
///
/// Construct an Asset object of type Notecard
///
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetNotecard(UUID assetID, byte[] assetData) : base(assetID, assetData)
{
Decode();
}
///
/// Construct an Asset object of type Notecard
///
/// A text string containing the raw contents of the notecard
public AssetNotecard(string text)
{
Text = text;
Encode();
}
///
/// Encode the raw contents of a string with the specific Linden Text properties
///
public override void Encode()
{
string temp = "Linden text version 2\n{\nLLEmbeddedItems version 1\n{\ncount 0\n}\nText length ";
temp += Text.Length + "\n";
temp += Text;
temp += "}";
AssetData = Utils.StringToBytes(temp);
}
///
/// Decode the raw asset data including the Linden Text properties
///
/// true if the AssetData was successfully decoded to a string
public override bool Decode()
{
Text = Utils.BytesToString(AssetData);
return true;
}
}
///
/// Represents an LSL Text object containing a string of UTF encoded characters
///
public class AssetScriptText : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.LSLText; } }
/// A string of characters represting the script contents
public string Source;
/// Initializes a new AssetScriptText object
public AssetScriptText() { }
///
/// Initializes a new AssetScriptText object with parameters
///
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetScriptText(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
///
/// Initializes a new AssetScriptText object with parameters
///
/// A string containing the scripts contents
public AssetScriptText(string source)
{
Source = source;
}
///
/// Encode a string containing the scripts contents into byte encoded AssetData
///
public override void Encode()
{
AssetData = Utils.StringToBytes(Source);
}
///
/// Decode a byte array containing the scripts contents into a string
///
/// true if decoding is successful
public override bool Decode()
{
Source = Utils.BytesToString(AssetData);
return true;
}
}
///
/// Represents an AssetScriptBinary object containing the
/// LSO compiled bytecode of an LSL script
///
public class AssetScriptBinary : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.LSLBytecode; } }
/// Initializes a new instance of an AssetScriptBinary object
public AssetScriptBinary() { }
/// Initializes a new instance of an AssetScriptBinary object with parameters
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetScriptBinary(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
AssetData = assetData;
}
///
/// TODO: Encodes a scripts contents into a LSO Bytecode file
///
public override void Encode() { }
///
/// TODO: Decode LSO Bytecode into a string
///
/// true
public override bool Decode() { return true; }
}
///
/// Represents a Sound Asset
///
public class AssetSound : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Sound; } }
/// Initializes a new instance of an AssetSound object
public AssetSound() { }
/// Initializes a new instance of an AssetSound object with parameters
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetSound(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
AssetData = assetData;
}
///
/// TODO: Encodes a sound file
///
public override void Encode() { }
///
/// TODO: Decode a sound file
///
/// true
public override bool Decode() { return true; }
}
///
/// Represents a texture
///
public class AssetTexture : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Texture; } }
/// A object containing image data
public ManagedImage Image;
///
public OpenJPEG.J2KLayerInfo[] LayerInfo;
///
public int Components;
/// Initializes a new instance of an AssetTexture object
public AssetTexture() { }
///
/// Initializes a new instance of an AssetTexture object
///
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetTexture(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
///
/// Initializes a new instance of an AssetTexture object
///
/// A object containing texture data
public AssetTexture(ManagedImage image)
{
Image = image;
Components = 0;
if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0)
Components += 3;
if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0)
++Components;
}
///
/// Populates the byte array with a JPEG2000
/// encoded image created from the data in
///
public override void Encode()
{
AssetData = OpenJPEG.Encode(Image);
}
///
/// Decodes the JPEG2000 data in AssetData to the
/// object
///
/// True if the decoding was successful, otherwise false
public override bool Decode()
{
Components = 0;
if (OpenJPEG.DecodeToImage(AssetData, out Image))
{
if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0)
Components += 3;
if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0)
++Components;
return true;
}
else
{
return false;
}
}
///
/// Decodes the begin and end byte positions for each quality layer in
/// the image
///
///
public bool DecodeLayerBoundaries()
{
return OpenJPEG.DecodeLayerBoundaries(AssetData, out LayerInfo, out Components);
}
}
///
/// Represents a primitive asset
///
public class AssetPrim : Asset
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Object; } }
/// Initializes a new instance of an AssetPrim object
public AssetPrim() { }
///
/// TODO:
///
public override void Encode() { }
///
/// TODO:
///
/// true
public override bool Decode() { return true; }
}
///
/// Represents a Wearable Asset, Clothing, Hair, Skin, Etc
///
public abstract class AssetWearable : Asset
{
/// A string containing the name of the asset
public string Name = String.Empty;
/// A string containing a short description of the asset
public string Description = String.Empty;
/// The Assets WearableType
public WearableType WearableType = WearableType.Shape;
/// The For-Sale status of the object
public SaleType ForSale;
/// An Integer representing the purchase price of the asset
public int SalePrice;
/// The of the assets creator
public UUID Creator;
/// The of the assets current owner
public UUID Owner;
/// The of the assets prior owner
public UUID LastOwner;
/// The of the Group this asset is set to
public UUID Group;
/// True if the asset is owned by a
public bool GroupOwned;
/// The Permissions mask of the asset
public Permissions Permissions;
/// A Dictionary containing Key/Value pairs of the objects parameters
public Dictionary Params = new Dictionary();
/// A Dictionary containing Key/Value pairs where the Key is the textures Index and the Value is the Textures
public Dictionary Textures = new Dictionary();
/// Initializes a new instance of an AssetWearable object
public AssetWearable() { }
/// Initializes a new instance of an AssetWearable object with parameters
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetWearable(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
/// Initializes a new instance of an AssetWearable object with parameters
/// A string containing the asset parameters
public AssetWearable(string source)
{
AssetData = Utils.StringToBytes(source);
}
///
/// Decode an assets byte encoded data to a string
///
/// true if the asset data was decoded successfully
public override bool Decode()
{
int version = -1;
Permissions = new Permissions();
string data = Utils.BytesToString(AssetData);
data = data.Replace("\r", String.Empty);
string[] lines = data.Split('\n');
for (int stri = 0; stri < lines.Length; stri++)
{
if (stri == 0)
{
string versionstring = lines[stri];
version = Int32.Parse(versionstring.Split(' ')[2]);
if (version != 22 && version != 18)
return false;
}
else if (stri == 1)
{
Name = lines[stri];
}
else if (stri == 2)
{
Description = lines[stri];
}
else
{
string line = lines[stri].Trim();
string[] fields = line.Split('\t');
if (fields.Length == 1)
{
fields = line.Split(' ');
if (fields[0] == "parameters")
{
int count = Int32.Parse(fields[1]) + stri;
for (; stri < count; )
{
stri++;
line = lines[stri].Trim();
fields = line.Split(' ');
int id = Int32.Parse(fields[0]);
if (fields[1] == ",")
fields[1] = "0";
else
fields[1] = fields[1].Replace(',', '.');
float weight = float.Parse(fields[1], System.Globalization.NumberStyles.Float,
Utils.EnUsCulture.NumberFormat);
Params[id] = weight;
}
}
else if (fields[0] == "textures")
{
int count = Int32.Parse(fields[1]) + stri;
for (; stri < count; )
{
stri++;
line = lines[stri].Trim();
fields = line.Split(' ');
AppearanceManager.TextureIndex id = (AppearanceManager.TextureIndex)Int32.Parse(fields[0]);
UUID texture = new UUID(fields[1]);
Textures[id] = texture;
}
}
else if (fields[0] == "type")
{
WearableType = (WearableType)Int32.Parse(fields[1]);
}
}
else if (fields.Length == 2)
{
switch (fields[0])
{
case "creator_mask":
// Deprecated, apply this as the base mask
Permissions.BaseMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "base_mask":
Permissions.BaseMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "owner_mask":
Permissions.OwnerMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "group_mask":
Permissions.GroupMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "everyone_mask":
Permissions.EveryoneMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "next_owner_mask":
Permissions.NextOwnerMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "creator_id":
Creator = new UUID(fields[1]);
break;
case "owner_id":
Owner = new UUID(fields[1]);
break;
case "last_owner_id":
LastOwner = new UUID(fields[1]);
break;
case "group_id":
Group = new UUID(fields[1]);
break;
case "group_owned":
GroupOwned = (Int32.Parse(fields[1]) != 0);
break;
case "sale_type":
ForSale = InventoryManager.StringToSaleType(fields[1]);
break;
case "sale_price":
SalePrice = Int32.Parse(fields[1]);
break;
case "sale_info":
// Container for sale_type and sale_price, ignore
break;
default:
return false;
}
}
}
}
return true;
}
///
/// Encode the assets string represantion into a format consumable by the asset server
///
public override void Encode()
{
const string NL = "\n";
StringBuilder data = new StringBuilder("LLWearable version 22\n");
data.Append(Name); data.Append(NL); data.Append(NL);
data.Append("\tpermissions 0\n\t{\n");
data.Append("\t\tbase_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.BaseMask)); data.Append(NL);
data.Append("\t\towner_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.OwnerMask)); data.Append(NL);
data.Append("\t\tgroup_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.GroupMask)); data.Append(NL);
data.Append("\t\teveryone_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.EveryoneMask)); data.Append(NL);
data.Append("\t\tnext_owner_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.NextOwnerMask)); data.Append(NL);
data.Append("\t\tcreator_id\t"); data.Append(Creator.ToString()); data.Append(NL);
data.Append("\t\towner_id\t"); data.Append(Owner.ToString()); data.Append(NL);
data.Append("\t\tlast_owner_id\t"); data.Append(LastOwner.ToString()); data.Append(NL);
data.Append("\t\tgroup_id\t"); data.Append(Group.ToString()); data.Append(NL);
if (GroupOwned) data.Append("\t\tgroup_owned\t1\n");
data.Append("\t}\n");
data.Append("\tsale_info\t0\n");
data.Append("\t{\n");
data.Append("\t\tsale_type\t"); data.Append(InventoryManager.SaleTypeToString(ForSale)); data.Append(NL);
data.Append("\t\tsale_price\t"); data.Append(SalePrice); data.Append(NL);
data.Append("\t}\n");
data.Append("type "); data.Append((int)WearableType); data.Append(NL);
data.Append("parameters "); data.Append(Params.Count); data.Append(NL);
foreach (KeyValuePair param in Params)
{
data.Append(param.Key); data.Append(" "); data.Append(Helpers.FloatToTerseString(param.Value)); data.Append(NL);
}
data.Append("textures "); data.Append(Textures.Count); data.Append(NL);
foreach (KeyValuePair texture in Textures)
{
data.Append(texture.Key); data.Append(" "); data.Append(texture.Value.ToString()); data.Append(NL);
}
AssetData = Utils.StringToBytes(data.ToString());
}
}
///
/// Represents an that can be worn on an avatar
/// such as a Shirt, Pants, etc.
///
public class AssetClothing : AssetWearable
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Clothing; } }
/// Initializes a new instance of an AssetScriptBinary object
public AssetClothing() { }
/// Initializes a new instance of an AssetScriptBinary object with parameters
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetClothing(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
/// Initializes a new instance of an AssetScriptBinary object with parameters
/// A string containing the Clothings data
public AssetClothing(string source) : base(source) { }
}
///
/// Represents an that represents an avatars body ie: Hair, Etc.
///
public class AssetBodypart : AssetWearable
{
/// Override the base classes AssetType
public override AssetType AssetType { get { return AssetType.Bodypart; } }
/// Initializes a new instance of an AssetBodyPart object
public AssetBodypart() { }
/// Initializes a new instance of an AssetBodyPart object with parameters
/// A unique specific to this asset
/// A byte array containing the raw asset data
public AssetBodypart(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
/// Initializes a new instance of an AssetBodyPart object with parameters
/// A string representing the values of the Bodypart
public AssetBodypart(string source) : base(source) { }
}
}