/* * 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) { } } }