diff --git a/Programs/Simian/InventoryDefinitions.cs b/Programs/Simian/InventoryDefinitions.cs new file mode 100644 index 00000000..e5d169bc --- /dev/null +++ b/Programs/Simian/InventoryDefinitions.cs @@ -0,0 +1,384 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Globalization; +using OpenMetaverse; + +namespace Simian +{ + [Flags] + public enum InventorySortOrder : uint + { + // ItemsByName = 0, + /// + ByDate = 1, + /// + FoldersByName = 2, + /// + SystemFoldersToTop = 4 + } + + #region Inventory Item Containers + + /// + /// Base class that inventory items and folders inherit from + /// + public abstract class InventoryObject + { + /// of the inventory item + public UUID ID; + /// of the parent folder + public UUID ParentID; + /// Item name + public string Name = String.Empty; + /// Item owner + public UUID OwnerID; + /// Parent folder + public InventoryObject Parent; + + public override int GetHashCode() + { + return ID.GetHashCode(); + } + } + + /// + /// Inventory item + /// + public class InventoryItem : InventoryObject + { + /// of the asset this item points to + public UUID AssetID; + /// The type of item from + public AssetType AssetType; + /// The type of item from the enum + public InventoryType InventoryType; + /// The of the creator of this item + public UUID CreatorID; + /// The s this item is set to or owned by + public UUID GroupID; + /// A Description of this item + public string Description = String.Empty; + /// If true, item is owned by a group + public bool GroupOwned; + /// The combined of this item + public Permissions Permissions; + /// The price this item can be purchased for + public int SalePrice; + /// The type of sale from the enum + public SaleType SaleType; + /// Combined flags from + public uint Flags; + /// Time and date this inventory item was created, stored as + /// UTC (Coordinated Universal Time) + public DateTime CreationDate; + + public override bool Equals(object obj) + { + if (!(obj is ItemData)) + return false; + + InventoryItem o = (InventoryItem)obj; + return o.ID == ID + && o.ParentID == ParentID + && o.Name == Name + && o.OwnerID == OwnerID + && o.AssetType == AssetType + && o.AssetID == AssetID + && o.CreationDate == CreationDate + && o.Description == Description + && o.Flags == Flags + && o.GroupID == GroupID + && o.GroupOwned == GroupOwned + && o.InventoryType == InventoryType + && o.Permissions.Equals(Permissions) + && o.SalePrice == SalePrice + && o.SaleType == SaleType; + } + + public static bool operator ==(InventoryItem lhs, InventoryItem rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(InventoryItem lhs, InventoryItem rhs) + { + return !(lhs == rhs); + } + + /// + /// Returns the ItemData in the hierarchical bracket format + /// used in the Second Life client's notecards and inventory cache. + /// + /// a string representation of this ItemData + public override string ToString() + { + StringWriter writer = new StringWriter(); + ToString(writer); + return writer.ToString(); + } + + /// + /// Writes the inventory item to the TextWriter in the hierarchical bracket format + /// used in the Second Life client's notecards and inventory cache. + /// + /// Writer to write to. + public void ToString(TextWriter writer) + { + writer.WriteLine("inv_item\t0"); + writer.WriteLine('{'); + writer.WriteLine("\titem_id\t{0}", ID.ToString()); + writer.WriteLine("\tparent_id\t{0}", ParentID.ToString()); + // Permissions: + writer.WriteLine("permissions\t0"); + writer.WriteLine('{'); + writer.WriteLine("\tbase_mask\t{0}", String.Format("{0:x}", (uint)Permissions.BaseMask).PadLeft(8, '0')); + writer.WriteLine("\towner_mask\t{0}", String.Format("{0:x}", (uint)Permissions.OwnerMask).PadLeft(8, '0')); + writer.WriteLine("\tgroup_mask\t{0}", String.Format("{0:x}", (uint)Permissions.GroupMask).PadLeft(8, '0')); + writer.WriteLine("\teveryone_mask\t{0}", String.Format("{0:x}", (uint)Permissions.EveryoneMask).PadLeft(8, '0')); + writer.WriteLine("\tnext_owner_mask\t{0}", String.Format("{0:x}", (uint)Permissions.NextOwnerMask).PadLeft(8, '0')); + writer.WriteLine("\tcreator_id\t{0}", CreatorID.ToString()); + writer.WriteLine("\towner_id\t{0}", OwnerID.ToString()); + writer.WriteLine("\tlast_owner_id\t{0}", UUID.Zero); // FIXME? + writer.WriteLine("\tgroup_id\t{0}", GroupID.ToString()); + writer.WriteLine('}'); + + writer.WriteLine("\tasset_id\t{0}", AssetID.ToString()); + writer.WriteLine("\ttype\t{0}", AssetTypeParser.StringValueOf(AssetType)); + writer.WriteLine("\tinv_type\t{0}", InventoryTypeParser.StringValueOf(InventoryType)); + writer.WriteLine("\tflags\t{0}", string.Format("{0:x}", Flags).PadLeft(8, '0')); + + // Sale info: + writer.WriteLine("sale_info\t0"); + writer.WriteLine('{'); + writer.WriteLine("\tsale_type\t{0}", SaleTypeParser.StringValueOf(SaleType)); + writer.WriteLine("\tsale_price\t{0}", SalePrice); + writer.WriteLine('}'); + + writer.WriteLine("\tname\t{0}|", Name); + writer.WriteLine("\tdesc\t{0}|", Description); + writer.WriteLine("\tcreation_date\t{0}", Utils.DateTimeToUnixTime(CreationDate)); + writer.WriteLine('}'); + } + + /// + /// Reads the ItemData from a string source. The string is wrapped + /// in a and passed to the + /// other method. + /// + /// String to parse ItemData from. + /// Parsed ItemData + public static ItemData Parse(string src) + { + return Parse(new StringReader(src)); + } + + /// + /// Reads an ItemData from a TextReader source. The format of the text + /// should be the same as the one used by Second Life Notecards. The TextReader should + /// be placed ideally on the line containing "inv_item" but parsing will succeed as long + /// as it is before the opening bracket immediately following the inv_item line. + /// The TextReader will be placed on the line following the inv_item's closing bracket. + /// + /// text source + /// Parsed item. + public static ItemData Parse(TextReader reader) + { + ItemData item = new ItemData(); + #region Parsing + TextData invItem = TextHierarchyParser.Parse(reader); + Console.WriteLine(invItem); + //if (invItem.Name == "inv_item") // YAY + item.UUID = new UUID(invItem.Nested["item_id"].Value); + item.ParentUUID = new UUID(invItem.Nested["parent_id"].Value); + item.AssetUUID = new UUID(invItem.Nested["asset_id"].Value); + item.AssetType = AssetTypeParser.Parse(invItem.Nested["type"].Value); + item.InventoryType = InventoryTypeParser.Parse(invItem.Nested["inv_type"].Value); + Utils.TryParseHex(invItem.Nested["flags"].Value, out item.Flags); + string rawName = invItem.Nested["name"].Value; + item.Name = rawName.Substring(0, rawName.LastIndexOf('|')); + string rawDesc = invItem.Nested["desc"].Value; + item.Description = rawDesc.Substring(0, rawDesc.LastIndexOf('|')); + item.CreationDate = Utils.UnixTimeToDateTime(uint.Parse(invItem.Nested["creation_date"].Value)); + + // Sale info: + TextData saleInfo = invItem.Nested["sale_info"]; + item.SalePrice = int.Parse(saleInfo.Nested["sale_price"].Value); + item.SaleType = SaleTypeParser.Parse(saleInfo.Nested["sale_type"].Value); + + TextData permissions = invItem.Nested["permissions"]; + item.Permissions = new Permissions(); + item.Permissions.BaseMask = (PermissionMask)uint.Parse(permissions.Nested["base_mask"].Value, NumberStyles.HexNumber); + item.Permissions.EveryoneMask = (PermissionMask)uint.Parse(permissions.Nested["everyone_mask"].Value, NumberStyles.HexNumber); + item.Permissions.GroupMask = (PermissionMask)uint.Parse(permissions.Nested["group_mask"].Value, NumberStyles.HexNumber); + item.Permissions.OwnerMask = (PermissionMask)uint.Parse(permissions.Nested["owner_mask"].Value, NumberStyles.HexNumber); + item.Permissions.NextOwnerMask = (PermissionMask)uint.Parse(permissions.Nested["next_owner_mask"].Value, NumberStyles.HexNumber); + item.CreatorID = new UUID(permissions.Nested["creator_id"].Value); + item.OwnerID = new UUID(permissions.Nested["owner_id"].Value); + item.GroupID = new UUID(permissions.Nested["group_id"].Value); + // permissions.Nested["last_owner_id"] // FIXME? + #endregion + return item; + } + + /// + /// + /// + /// String to parse from. + /// Parsed ItemData. + /// true if successful, false otherwise. + public static bool TryParse(string str, out ItemData item) + { + return TryParse(new StringReader(str), out item); + } + + /// + /// + /// + /// Text source. + /// Parsed ItemData. + /// true if successful false otherwise. + public static bool TryParse(TextReader reader, out ItemData item) + { + try + { + item = Parse(reader); + } + catch (Exception e) + { + item = new ItemData(); + Logger.Log(e.Message, Helpers.LogLevel.Error, e); + return false; + } + + return true; + } + } + + /// + /// Inventory folder + /// + public class InventoryFolder : InventoryObject + { + /// The Preferred for a folder. + public AssetType PreferredType; + /// The Version of this folder + public int Version; + /// Number of child items this folder contains + public InternalDictionary Children = new InternalDictionary(); + + public override bool Equals(object obj) + { + if (!(obj is FolderData)) + return false; + + InventoryFolder o = (InventoryFolder)obj; + return o.ID == ID; + } + + public static bool operator ==(InventoryFolder lhs, InventoryFolder rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(InventoryFolder lhs, InventoryFolder rhs) + { + return !(lhs == rhs); + } + + /// + /// Returns the FolderData in the hierarchical bracket format + /// used in the Second Life client's notecards and inventory cache. + /// + /// a string representation of this FolderData + public override string ToString() + { + StringWriter writer = new StringWriter(); + ToString(writer); + return writer.ToString(); + } + + /// + /// Writes the FolderData to the TextWriter in the hierarchical bracket format + /// used in the Second Life client's notecards and inventory cache. + /// + /// Writer to write to. + public void ToString(TextWriter writer) + { + writer.WriteLine("inv_category\t0"); + writer.WriteLine('{'); + writer.WriteLine("\tcat_id\t{0}", ID.ToString()); + writer.WriteLine("\tparent_id\t{0}", ParentID.ToString()); + writer.WriteLine("\ttype\tcategory"); + // TODO: Some folders have "-1" as their perf_type, investigate this. + writer.WriteLine("\tpref_type\t{0}", AssetTypeParser.StringValueOf(PreferredType)); + writer.WriteLine("\tname\t{0}|", Name); + writer.WriteLine("\towner_id\t{0}", OwnerID.ToString()); + writer.WriteLine("\tversion\t{0}", Version); + writer.WriteLine('}'); + } + + /// + /// Reads the FolderData from a string source. The string is wrapped + /// in a and passed to the + /// other method. + /// + /// String to parse FolderData from. + /// Parsed FolderData + public static FolderData Parse(string src) + { + return Parse(new StringReader(src)); + } + + /// + /// Reads an InventoryItem from a TextReader source. The format of the text + /// should be the same as the one used by Second Life Notecards. The TextReader should + /// be placed ideally on the line containing "inv_category" but parsing will succeed as long + /// as it is before the opening bracket immediately following the inv_category line. + /// The TextReader will be placed on the line following the inv_category's closing bracket. + /// + /// text source + /// Parsed item. + public static FolderData Parse(TextReader reader) + { + FolderData folder = new FolderData(); + #region Parsing + TextData invCategory = TextHierarchyParser.Parse(reader); + + //if (invCategory.Name == "inv_category") // YAY + folder.UUID = new UUID(invCategory.Nested["cat_id"].Value); + string rawName = invCategory.Nested["name"].Value; + folder.Name = rawName.Substring(0, rawName.LastIndexOf('|')); + folder.OwnerID = new UUID(invCategory.Nested["owner_id"].Value); + folder.ParentUUID = new UUID(invCategory.Nested["parent_id"].Value); + folder.PreferredType = AssetTypeParser.Parse(invCategory.Nested["pref_type"].Value); + folder.Version = int.Parse(invCategory.Nested["version"].Value); + // TODO: Investigate invCategory.Nested["type"] + #endregion + return folder; + } + + public static bool TryParse(string str, out FolderData folder) + { + return TryParse(new StringReader(str), out folder); + } + + public static bool TryParse(TextReader reader, out FolderData folder) + { + try + { + folder = Parse(reader); + } + catch (Exception e) + { + folder = new FolderData(); + Logger.Log(e.Message, Helpers.LogLevel.Error, e); + return false; + } + return true; + } + } + + #endregion Inventory Item Containers +}