/* * Copyright (c) 2006, Second Life Reverse Engineering Team * 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 Second Life Reverse Engineering Team 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.Collections.Generic; using libsecondlife; namespace libsecondlife.AssetSystem { /// /// Asset for wearables such as Socks, Eyes, Gloves, Hair, Pants, Shape, Shirt, Shoes, Skin, Jacket, Skirt, Underpants /// public class AssetWearable : Asset { private string _Name = ""; public string Name { get { return _Name; } set { _Name = value; UpdateAssetData(); } } private string _Description = ""; public string Description { get { return _Description; } set { _Description = value; UpdateAssetData(); } } private AppearanceLayerType _AppearanceLayer = 0; public AppearanceLayerType AppearanceLayer { get { return _AppearanceLayer; } set { _AppearanceLayer = value; UpdateAssetData(); } } private string _Sale_Type = "not"; public string Sale_Type { get { return _Sale_Type; } set { _Sale_Type = value; UpdateAssetData(); } } private uint _Sale_Price = 10; public uint Sale_Price { get { return _Sale_Price; } set { _Sale_Price = value; UpdateAssetData(); } } private LLUUID _Creator_ID = new LLUUID(); public LLUUID Creator_ID { get { return _Creator_ID; } set { _Creator_ID = value; UpdateAssetData(); } } private LLUUID _Owner_ID = new LLUUID(); public LLUUID Owner_ID { get { return _Owner_ID; } set { _Owner_ID = value; UpdateAssetData(); } } private LLUUID _Last_Owner_ID = new LLUUID(); public LLUUID Last_Owner_ID { get { return _Last_Owner_ID; } set { _Last_Owner_ID = value; UpdateAssetData(); } } private LLUUID _Group_ID = new LLUUID(); public LLUUID Group_ID { get { return _Group_ID; } set { _Group_ID = value; UpdateAssetData(); } } private bool _Group_Owned = false; public bool Group_Owned { get { return _Group_Owned; } set { _Group_Owned = value; UpdateAssetData(); } } private uint _Permission_Base_Mask = 0; public uint Permission_Base_Mask { get { return _Permission_Base_Mask; } set { _Permission_Base_Mask = value; UpdateAssetData(); } } private uint _Permission_Owner_Mask = 0; public uint Permission_Owner_Mask { get { return _Permission_Owner_Mask; } set { _Permission_Owner_Mask = value; UpdateAssetData(); } } private uint _Permission_Group_Mask = 0; public uint Permission_Group_Mask { get { return _Permission_Group_Mask; } set { _Permission_Group_Mask = value; UpdateAssetData(); } } private uint _Permission_Everyone_Mask = 0; public uint Permission_Everyone_Mask { get { return _Permission_Everyone_Mask; } set { _Permission_Everyone_Mask = value; UpdateAssetData(); } } private uint _Permission_Next_Owner_Mask = 0; public uint Permission_Next_Owner_Mask { get { return _Permission_Next_Owner_Mask; } set { _Permission_Next_Owner_Mask = value; UpdateAssetData(); } } private Dictionary _Parameters = new Dictionary(); public Dictionary Parameters { get { return _Parameters; } set { _Parameters = value; UpdateAssetData(); } } private Dictionary _Textures = new Dictionary(); public Dictionary Textures { get { return _Textures; } set { _Textures = value; UpdateAssetData(); } } private string[] _ForSaleNames = new string[] { "not", "orig", "copy", "cntn" }; private enum _ForSale { /// Not for sale Not = 0, /// The original is for sale Original = 1, /// Copies are for sale Copy = 2, /// The contents of the object are for sale Contents = 3 } private _ForSale _Sale = _ForSale.Not; private int _SalePrice = 0; public enum AppearanceLayerType : byte { /// Shape = 0, /// Skin, /// Hair, /// Eyes, /// Shirt, /// Pants, /// Shoes, /// Socks, /// Jacket, /// Gloves, /// Undershirt, /// Underpants, /// Skirt, /// Invalid = 255 }; /// /// /// /// public AssetWearable(LLUUID assetID, sbyte assetType, byte[] assetData) : base(assetID, assetType, false, assetData) { UpdateFromAssetData(); } /// /// Converts byte[] data from a data transfer into a bodypart class /// /// internal void UpdateFromAssetData() { if ( AssetData == null || AssetData.Length == 0) { return; } string wearableData = Helpers.FieldToUTF8String(this._AssetData); int version = -1; int n = -1; try { n = wearableData.IndexOf('\n'); version = Int32.Parse(wearableData.Substring(19, n - 18)); wearableData = wearableData.Remove(0, n); if (version != 22) { Console.WriteLine("** WARNING ** : Wearable asset has unrecognized version " + version); return; } n = wearableData.IndexOf('\n'); Name = wearableData.Substring(0, n); wearableData = wearableData.Remove(0, n); n = wearableData.IndexOf('\n'); Description = wearableData.Substring(0, n); wearableData = wearableData.Remove(0, n); // Split in to an upper and lower half string[] parts = wearableData.Split(new string[] { "parameters" }, StringSplitOptions.None); parts[1] = "parameters" + parts[1]; // Parse the upper half string[] lines = parts[0].Split('\n'); foreach (string thisline in lines) { string line = thisline.Trim(); string[] fields = line.Split('\t'); if (fields.Length == 2) { if (fields[0] == "creator_mask") { // Deprecated, apply this as the base mask _Permission_Base_Mask = UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber); } else if (fields[0] == "base_mask") { _Permission_Base_Mask = UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber); } else if (fields[0] == "owner_mask") { _Permission_Owner_Mask = UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber); } else if (fields[0] == "group_mask") { _Permission_Group_Mask = UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber); } else if (fields[0] == "everyone_mask") { _Permission_Everyone_Mask = UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber); } else if (fields[0] == "next_owner_mask") { _Permission_Next_Owner_Mask = UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber); } else if (fields[0] == "creator_id") { _Creator_ID = new LLUUID(fields[1]); } else if (fields[0] == "owner_id") { _Owner_ID = new LLUUID(fields[1]); } else if (fields[0] == "last_owner_id") { _Last_Owner_ID = new LLUUID(fields[1]); } else if (fields[0] == "group_id") { _Group_ID = new LLUUID(fields[1]); } else if (fields[0] == "group_owned") { _Group_Owned = (Int32.Parse(fields[1]) != 0); } else if (fields[0] == "sale_type") { for (int i = 0; i < _ForSaleNames.Length; i++) { if (fields[1] == _ForSaleNames[i]) { _Sale = (_ForSale)i; break; } } } else if (fields[0] == "sale_price") { _SalePrice = Int32.Parse(fields[1]); } else if (fields[0] == "perm_mask") { Console.WriteLine("** WARNING ** : Wearable asset has deprecated perm_mask field, ignoring"); } } else if (line.StartsWith("type ")) { AppearanceLayer = (AppearanceLayerType)Int32.Parse(line.Substring(5)); break; } } // Break up the lower half in to parameters and textures string[] lowerparts = parts[1].Split(new string[] { "textures" }, StringSplitOptions.None); lowerparts[1] = "textures" + lowerparts[1]; // Parse the parameters lines = lowerparts[0].Split('\n'); foreach (string line in lines) { string[] fields = line.Split(' '); // Use exception handling to deal with all the lines we aren't interested in try { int id = Int32.Parse(fields[0]); float weight = Single.Parse(fields[1]); _Parameters[id] = weight; } catch (Exception) { } } // Parse the textures lines = lowerparts[1].Split('\n'); foreach (string line in lines) { string[] fields = line.Split(' '); // Use exception handling to deal with all the lines we aren't interested in try { uint id = UInt32.Parse(fields[0]); LLUUID texture = LLUUID.Parse(fields[1]); _Textures[id] = texture; } catch (Exception) { } } return; } catch (Exception e) { Console.WriteLine("** WARNING **", "Failed to parse wearable asset: " + e.ToString()); } return; } private void UpdateAssetData() { string data = "LLWearable version 22\n"; data += this._Name + "\n\n"; data += "\tpermissions 0\n\t{\n"; data += "\t\tbase_mask\t" + uintToHex(this._Permission_Base_Mask); data += "\n\t\towner_mask\t" + uintToHex(this._Permission_Owner_Mask); data += "\n\t\tgroup_mask\t" + uintToHex(this._Permission_Group_Mask); data += "\n\t\teveryone_mask\t" + uintToHex(this._Permission_Everyone_Mask); data += "\n\t\tnext_owner_mask\t" + uintToHex(this._Permission_Next_Owner_Mask); data += "\n\t\tcreator_id\t" + this._Creator_ID.ToStringHyphenated(); data += "\n\t\towner_id\t" + this._Owner_ID.ToStringHyphenated(); data += "\n\t\tlast_owner_id\t" + this._Last_Owner_ID.ToStringHyphenated(); data += "\n\t\tgroup_id\t" + this._Group_ID.ToStringHyphenated(); data += "\n\t}"; data += "\n\tsale_info\t0"; data += "\n\t{"; data += "\n\t\tsale_type\t" + this._Sale_Type; data += "\n\t\tsale_price\t" + this._Sale_Price; data += "\n\t}"; data += "\ntype " + this._AppearanceLayer; data += "\nparameters " + this._Parameters.Count; foreach (KeyValuePair param in this._Parameters) { string prm = string.Format("{0:f1}", param.Value); if (prm == "-1.0" || prm == "1.0" || prm == "0.0") { switch (prm) { case "-1.0": prm = "-1"; break; case "0.0": prm = "0"; break; case "1.0": prm = "1"; break; } } data += "\n" + param.Key + " " + prm; } data += "\ntextures " + this._Textures.Count; foreach (KeyValuePair texture in this._Textures) { data += "\n" + texture.Key + " " + texture.Value.ToStringHyphenated(); } _AssetData = System.Text.Encoding.ASCII.GetBytes(data.ToCharArray()); } private static string uintToHex(uint i) { return string.Format("{0:x8}", i); } public override void SetAssetData(byte[] data) { _AssetData = data; if ( (_AssetData != null) && (_AssetData.Length > 0) ) { UpdateFromAssetData(); } } } }