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
}