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
+}