diff --git a/libsecondlife-cs/AvatarManager.cs b/libsecondlife-cs/AvatarManager.cs
index 0c903722..b56bb381 100644
--- a/libsecondlife-cs/AvatarManager.cs
+++ b/libsecondlife-cs/AvatarManager.cs
@@ -1,474 +1,474 @@
-/*
- * 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 System.Threading;
-using libsecondlife.Packets;
-
-namespace libsecondlife
-{
- ///
- /// Retrieve friend status notifications, and retrieve avatar names and
- /// profiles
- ///
- public class AvatarManager
- {
- ///
- /// Triggered after friend request packet is sent out
- ///
- ///
- ///
- public delegate void FriendNotificationCallback(LLUUID agentID, bool online);
- ///
- /// Triggered when a UUIDNameReply is received
- ///
- ///
- public delegate void AvatarNamesCallback(Dictionary names);
- ///
- /// Triggered when a response for avatar statistics (ratings) is returned
- ///
- ///
- ///
- public delegate void AvatarStatisticsCallback(LLUUID avatarID, Avatar.Statistics statistics);
- ///
- /// Triggered when a response for avatar interests is returned
- ///
- ///
- ///
- public delegate void AvatarInterestsCallback(LLUUID avatarID, Avatar.Interests interests);
- ///
- /// Triggered when avatar properties are received (AvatarPropertiesReply)
- ///
- ///
- ///
- public delegate void AvatarPropertiesCallback(LLUUID avatarID, Avatar.AvatarProperties properties);
- ///
- /// Triggered when an avatar group list is received (AvatarGroupsReply)
- ///
- ///
- ///
- public delegate void AvatarGroupsCallback(LLUUID avatarID, AvatarGroupsReplyPacket.GroupDataBlock[] groups);
- ///
- /// Triggered when a name search reply is received (AvatarPickerReply)
- ///
- ///
- ///
- public delegate void AvatarNameSearchCallback(LLUUID queryID, Dictionary avatars);
-
-
- /// Triggered whenever a friend comes online or goes offline
- public event FriendNotificationCallback OnFriendNotification;
- ///
- public event AvatarNamesCallback OnAvatarNames;
- ///
- public event AvatarStatisticsCallback OnAvatarStatistics;
- ///
- public event AvatarInterestsCallback OnAvatarInterests;
- ///
- public event AvatarPropertiesCallback OnAvatarProperties;
- ///
- public event AvatarGroupsCallback OnAvatarGroups;
- ///
- public event AvatarNameSearchCallback OnAvatarNameSearch;
-
- private SecondLife Client;
-
- ///
- /// Represents other avatars
- ///
- ///
- public AvatarManager(SecondLife client)
- {
- Client = client;
-
- // Friend notification callback
- NetworkManager.PacketCallback callback = new NetworkManager.PacketCallback(FriendNotificationHandler);
- Client.Network.RegisterCallback(PacketType.OnlineNotification, callback);
- Client.Network.RegisterCallback(PacketType.OfflineNotification, callback);
-
- // Avatar profile callbacks
- Client.Network.RegisterCallback(PacketType.AvatarPropertiesReply, new NetworkManager.PacketCallback(AvatarPropertiesHandler));
- Client.Network.RegisterCallback(PacketType.AvatarStatisticsReply, new NetworkManager.PacketCallback(AvatarStatisticsHandler));
- Client.Network.RegisterCallback(PacketType.AvatarInterestsReply, new NetworkManager.PacketCallback(AvatarInterestsHandler));
-
- // Avatar group callback
- Client.Network.RegisterCallback(PacketType.AvatarGroupsReply, new NetworkManager.PacketCallback(AvatarGroupsHandler));
-
- // Viewer effect callback
- Client.Network.RegisterCallback(PacketType.ViewerEffect, new NetworkManager.PacketCallback(ViewerEffectHandler));
-
- // Other callbacks
- Client.Network.RegisterCallback(PacketType.UUIDNameReply, new NetworkManager.PacketCallback(AvatarNameHandler));
- Client.Network.RegisterCallback(PacketType.AvatarPickerReply, new NetworkManager.PacketCallback(AvatarPickerReplyHandler));
- }
-
- ///
- /// Request a single avatar name
- ///
- /// The avatar key to retrieve a name for
- public void RequestAvatarName(LLUUID id)
- {
- UUIDNameRequestPacket request = new UUIDNameRequestPacket();
- request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[1];
- request.UUIDNameBlock[0] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
- request.UUIDNameBlock[0].ID = id;
-
- Client.Network.SendPacket(request);
- }
-
- ///
- /// Request a list of avatar names
- ///
- /// The avatar keys to retrieve names for
- public void RequestAvatarNames(List ids)
- {
- UUIDNameRequestPacket request = new UUIDNameRequestPacket();
- request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[ids.Count];
-
- for (int i = 0; i < ids.Count; i++)
- {
- request.UUIDNameBlock[i] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
- request.UUIDNameBlock[i].ID = ids[i];
- }
-
- Client.Network.SendPacket(request);
- }
-
- ///
- /// Start a request for Avatar Properties
- ///
- ///
- public void RequestAvatarProperties(LLUUID avatarid)
- {
- AvatarPropertiesRequestPacket aprp = new AvatarPropertiesRequestPacket();
-
- aprp.AgentData.AgentID = Client.Network.AgentID;
- aprp.AgentData.SessionID = Client.Network.SessionID;
- aprp.AgentData.AvatarID = avatarid;
-
- Client.Network.SendPacket(aprp);
- }
-
- ///
- /// Search for an avatar (first name, last name, and uuid)
- ///
- /// The name to search for
- /// An ID to associate with this query
- public void RequestAvatarNameSearch(string name, LLUUID queryID)
- {
- AvatarPickerRequestPacket aprp = new AvatarPickerRequestPacket();
-
- aprp.AgentData.AgentID = Client.Network.AgentID;
- aprp.AgentData.SessionID = Client.Network.SessionID;
- aprp.AgentData.QueryID = queryID;
- aprp.Data.Name = Helpers.StringToField(name);
-
- Client.Network.SendPacket(aprp);
- }
-
- ///
- /// Process an incoming UUIDNameReply Packet and insert Full Names into the Avatars Dictionary
- ///
- /// Incoming Packet to process
- /// Unused
- private void AvatarNameHandler(Packet packet, Simulator simulator)
- {
- if (OnAvatarNames != null)
- {
- Dictionary names = new Dictionary();
- UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet;
-
- foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock)
- {
- names[block.ID] = Helpers.FieldToUTF8String(block.FirstName) +
- " " + Helpers.FieldToUTF8String(block.LastName);
- }
-
- OnAvatarNames(names);
- }
- }
-
- ///
- /// Handle incoming friend notifications
- ///
- ///
- ///
- private void FriendNotificationHandler(Packet packet, Simulator simulator)
- {
- if (OnFriendNotification != null)
- {
- if (packet.Type == PacketType.OnlineNotification)
- {
- // If the agent is online
- foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock)
- OnFriendNotification(block.AgentID, true);
- }
- else if (packet.Type == PacketType.OfflineNotification)
- {
- // If the agent is offline
- foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock)
- OnFriendNotification(block.AgentID, true);
- }
- }
- }
-
- ///
- /// Handles incoming avatar statistics, such as ratings
- ///
- ///
- ///
- private void AvatarStatisticsHandler(Packet packet, Simulator simulator)
- {
- if (OnAvatarStatistics != null)
- {
- AvatarStatisticsReplyPacket asr = (AvatarStatisticsReplyPacket)packet;
- Avatar.Statistics stats = new Avatar.Statistics();
-
- foreach (AvatarStatisticsReplyPacket.StatisticsDataBlock b in asr.StatisticsData)
- {
- string n = Helpers.FieldToUTF8String(b.Name);
-
- switch (n)
- {
- case "Behavior":
- stats.BehaviorPositive = b.Positive;
- stats.BehaviorNegative = b.Negative;
- break;
- case "Appearance":
- stats.AppearancePositive = b.Positive;
- stats.AppearanceNegative = b.Negative;
- break;
- case "Building":
- stats.AppearancePositive = b.Positive;
- stats.AppearanceNegative = b.Negative;
- break;
- case "Given":
- stats.GivenPositive = b.Positive;
- stats.GivenNegative = b.Negative;
- break;
- default:
- Client.Log("Got an AvatarStatistics block with the name " + n, Helpers.LogLevel.Warning);
- break;
- }
- }
-
- OnAvatarStatistics(asr.AvatarData.AvatarID, stats);
- }
- }
-
- ///
- /// Process incoming avatar properties (profile data)
- ///
- ///
- ///
- private void AvatarPropertiesHandler(Packet packet, Simulator sim)
- {
- if (OnAvatarProperties != null)
- {
- AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet;
- Avatar.AvatarProperties properties = new Avatar.AvatarProperties();
-
- properties.ProfileImage = reply.PropertiesData.ImageID;
- properties.FirstLifeImage = reply.PropertiesData.FLImageID;
- properties.Partner = reply.PropertiesData.PartnerID;
- properties.AboutText = Helpers.FieldToUTF8String(reply.PropertiesData.AboutText);
- properties.FirstLifeText = Helpers.FieldToUTF8String(reply.PropertiesData.FLAboutText);
- properties.BornOn = Helpers.FieldToUTF8String(reply.PropertiesData.BornOn);
- properties.CharterMember = Helpers.FieldToUTF8String(reply.PropertiesData.CharterMember);
- // FIXME: These have been converted in to a Flags field, build an enum and fix this!
- //properties.AllowPublish = reply.PropertiesData.AllowPublish;
- //properties.MaturePublish = reply.PropertiesData.MaturePublish;
- //properties.Identified = reply.PropertiesData.Identified;
- //properties.Transacted = reply.PropertiesData.Transacted;
- properties.ProfileURL = Helpers.FieldToUTF8String(reply.PropertiesData.ProfileURL);
-
- OnAvatarProperties(reply.AgentData.AvatarID, properties);
- }
- }
-
- ///
- /// Process incoming Avatar Interests information
- ///
- private void AvatarInterestsHandler(Packet packet, Simulator simulator)
- {
- if (OnAvatarInterests != null)
- {
- AvatarInterestsReplyPacket airp = (AvatarInterestsReplyPacket)packet;
- Avatar.Interests interests = new Avatar.Interests();
-
- interests.WantToMask = airp.PropertiesData.WantToMask;
- interests.WantToText = Helpers.FieldToUTF8String(airp.PropertiesData.WantToText);
- interests.SkillsMask = airp.PropertiesData.SkillsMask;
- interests.SkillsText = Helpers.FieldToUTF8String(airp.PropertiesData.SkillsText);
- interests.LanguagesText = Helpers.FieldToUTF8String(airp.PropertiesData.LanguagesText);
-
- OnAvatarInterests(airp.AgentData.AvatarID, interests);
- }
- }
-
- private void AvatarGroupsHandler(Packet packet, Simulator simulator)
- {
- if (OnAvatarGroups != null)
- {
- AvatarGroupsReplyPacket groups = (AvatarGroupsReplyPacket)packet;
-
- // FIXME: Build a little struct to represent the groups.GroupData blocks so we keep
- // libsecondlife.Packets abstracted away
- OnAvatarGroups(groups.AgentData.AvatarID, groups.GroupData);
- }
- }
-
- private void AvatarPickerReplyHandler(Packet packet, Simulator simulator)
- {
- if (OnAvatarNameSearch != null)
- {
- AvatarPickerReplyPacket reply = (AvatarPickerReplyPacket)packet;
- Dictionary avatars = new Dictionary();
-
- foreach (AvatarPickerReplyPacket.DataBlock block in reply.Data)
- {
- avatars[block.AvatarID] = Helpers.FieldToUTF8String(block.FirstName) +
- " " + Helpers.FieldToUTF8String(block.LastName);
- }
-
- OnAvatarNameSearch(reply.AgentData.QueryID, avatars);
- }
- }
-
- ///
- /// Process an incoming effect
- ///
- private void ViewerEffectHandler(Packet packet, Simulator simulator)
- {
- ViewerEffectPacket effect = (ViewerEffectPacket)packet;
-
- foreach (ViewerEffectPacket.EffectBlock block in effect.Effect)
- {
- MainAvatar.EffectType type;
-
- try
- {
- type = (MainAvatar.EffectType)block.Type;
- }
- catch (Exception)
- {
- Client.Log("Received a ViewerEffect block with an unknown type " + block.Type,
- Helpers.LogLevel.Warning);
- continue;
- }
-
- //LLColor color;
- //if (block.Color.Length == 4)
- //{
- // color = new LLColor(block.Color, 0);
- //}
- //else
- //{
- // Client.Log("Received a ViewerEffect.EffectBlock.Color array with " + block.Color.Length + " bytes",
- // Helpers.LogLevel.Warning);
- // color = new LLColor();
- //}
-
- // Each ViewerEffect type uses it's own custom binary format for additional data. Fun eh?
- switch (type)
- {
- case MainAvatar.EffectType.Text:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Icon:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Connector:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.FlexibleObject:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.AnimalControls:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.AnimationObject:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Cloth:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Beam:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Glow:
- Client.Log("Received a Glow ViewerEffect which is not implemented yet",
- Helpers.LogLevel.Warning);
- break;
- case MainAvatar.EffectType.Point:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Trail:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Sphere:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Spiral:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.Edit:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.LookAt:
- //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
- break;
- case MainAvatar.EffectType.PointAt:
- if (block.TypeData.Length == 57)
- {
- LLUUID sourceAvatar = new LLUUID(block.TypeData, 0);
- LLUUID targetObject = new LLUUID(block.TypeData, 16);
- LLVector3d targetPos = new LLVector3d(block.TypeData, 32);
- MainAvatar.PointAtType pointAt;
- try
- {
- pointAt = (MainAvatar.PointAtType)block.TypeData[56];
- }
- catch (Exception)
- {
- Client.Log("Unrecognized PointAtType " + block.TypeData[56], Helpers.LogLevel.Warning);
- pointAt = MainAvatar.PointAtType.Clear;
- }
-
- // TODO: Create a OnAvatarPointAt event and call it here
- }
- else
- {
- Client.Log("Received a PointAt ViewerEffect with an incorrect TypeData size of " +
- block.TypeData.Length + " bytes", Helpers.LogLevel.Warning);
- }
- break;
- }
- }
- }
- }
-}
+/*
+ * 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 System.Threading;
+using libsecondlife.Packets;
+
+namespace libsecondlife
+{
+ ///
+ /// Retrieve friend status notifications, and retrieve avatar names and
+ /// profiles
+ ///
+ public class AvatarManager
+ {
+ ///
+ /// Triggered after friend request packet is sent out
+ ///
+ ///
+ ///
+ public delegate void FriendNotificationCallback(LLUUID agentID, bool online);
+ ///
+ /// Triggered when a UUIDNameReply is received
+ ///
+ ///
+ public delegate void AvatarNamesCallback(Dictionary names);
+ ///
+ /// Triggered when a response for avatar statistics (ratings) is returned
+ ///
+ ///
+ ///
+ public delegate void AvatarStatisticsCallback(LLUUID avatarID, Avatar.Statistics statistics);
+ ///
+ /// Triggered when a response for avatar interests is returned
+ ///
+ ///
+ ///
+ public delegate void AvatarInterestsCallback(LLUUID avatarID, Avatar.Interests interests);
+ ///
+ /// Triggered when avatar properties are received (AvatarPropertiesReply)
+ ///
+ ///
+ ///
+ public delegate void AvatarPropertiesCallback(LLUUID avatarID, Avatar.AvatarProperties properties);
+ ///
+ /// Triggered when an avatar group list is received (AvatarGroupsReply)
+ ///
+ ///
+ ///
+ public delegate void AvatarGroupsCallback(LLUUID avatarID, AvatarGroupsReplyPacket.GroupDataBlock[] groups);
+ ///
+ /// Triggered when a name search reply is received (AvatarPickerReply)
+ ///
+ ///
+ ///
+ public delegate void AvatarNameSearchCallback(LLUUID queryID, Dictionary avatars);
+
+
+ /// Triggered whenever a friend comes online or goes offline
+ public event FriendNotificationCallback OnFriendNotification;
+ ///
+ public event AvatarNamesCallback OnAvatarNames;
+ ///
+ public event AvatarStatisticsCallback OnAvatarStatistics;
+ ///
+ public event AvatarInterestsCallback OnAvatarInterests;
+ ///
+ public event AvatarPropertiesCallback OnAvatarProperties;
+ ///
+ public event AvatarGroupsCallback OnAvatarGroups;
+ ///
+ public event AvatarNameSearchCallback OnAvatarNameSearch;
+
+ private SecondLife Client;
+
+ ///
+ /// Represents other avatars
+ ///
+ ///
+ public AvatarManager(SecondLife client)
+ {
+ Client = client;
+
+ // Friend notification callback
+ NetworkManager.PacketCallback callback = new NetworkManager.PacketCallback(FriendNotificationHandler);
+ Client.Network.RegisterCallback(PacketType.OnlineNotification, callback);
+ Client.Network.RegisterCallback(PacketType.OfflineNotification, callback);
+
+ // Avatar profile callbacks
+ Client.Network.RegisterCallback(PacketType.AvatarPropertiesReply, new NetworkManager.PacketCallback(AvatarPropertiesHandler));
+ Client.Network.RegisterCallback(PacketType.AvatarStatisticsReply, new NetworkManager.PacketCallback(AvatarStatisticsHandler));
+ Client.Network.RegisterCallback(PacketType.AvatarInterestsReply, new NetworkManager.PacketCallback(AvatarInterestsHandler));
+
+ // Avatar group callback
+ Client.Network.RegisterCallback(PacketType.AvatarGroupsReply, new NetworkManager.PacketCallback(AvatarGroupsHandler));
+
+ // Viewer effect callback
+ Client.Network.RegisterCallback(PacketType.ViewerEffect, new NetworkManager.PacketCallback(ViewerEffectHandler));
+
+ // Other callbacks
+ Client.Network.RegisterCallback(PacketType.UUIDNameReply, new NetworkManager.PacketCallback(AvatarNameHandler));
+ Client.Network.RegisterCallback(PacketType.AvatarPickerReply, new NetworkManager.PacketCallback(AvatarPickerReplyHandler));
+ }
+
+ ///
+ /// Request a single avatar name
+ ///
+ /// The avatar key to retrieve a name for
+ public void RequestAvatarName(LLUUID id)
+ {
+ UUIDNameRequestPacket request = new UUIDNameRequestPacket();
+ request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[1];
+ request.UUIDNameBlock[0] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
+ request.UUIDNameBlock[0].ID = id;
+
+ Client.Network.SendPacket(request);
+ }
+
+ ///
+ /// Request a list of avatar names
+ ///
+ /// The avatar keys to retrieve names for
+ public void RequestAvatarNames(List ids)
+ {
+ UUIDNameRequestPacket request = new UUIDNameRequestPacket();
+ request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[ids.Count];
+
+ for (int i = 0; i < ids.Count; i++)
+ {
+ request.UUIDNameBlock[i] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
+ request.UUIDNameBlock[i].ID = ids[i];
+ }
+
+ Client.Network.SendPacket(request);
+ }
+
+ ///
+ /// Start a request for Avatar Properties
+ ///
+ ///
+ public void RequestAvatarProperties(LLUUID avatarid)
+ {
+ AvatarPropertiesRequestPacket aprp = new AvatarPropertiesRequestPacket();
+
+ aprp.AgentData.AgentID = Client.Network.AgentID;
+ aprp.AgentData.SessionID = Client.Network.SessionID;
+ aprp.AgentData.AvatarID = avatarid;
+
+ Client.Network.SendPacket(aprp);
+ }
+
+ ///
+ /// Search for an avatar (first name, last name, and uuid)
+ ///
+ /// The name to search for
+ /// An ID to associate with this query
+ public void RequestAvatarNameSearch(string name, LLUUID queryID)
+ {
+ AvatarPickerRequestPacket aprp = new AvatarPickerRequestPacket();
+
+ aprp.AgentData.AgentID = Client.Network.AgentID;
+ aprp.AgentData.SessionID = Client.Network.SessionID;
+ aprp.AgentData.QueryID = queryID;
+ aprp.Data.Name = Helpers.StringToField(name);
+
+ Client.Network.SendPacket(aprp);
+ }
+
+ ///
+ /// Process an incoming UUIDNameReply Packet and insert Full Names into the Avatars Dictionary
+ ///
+ /// Incoming Packet to process
+ /// Unused
+ private void AvatarNameHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarNames != null)
+ {
+ Dictionary names = new Dictionary();
+ UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet;
+
+ foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock)
+ {
+ names[block.ID] = Helpers.FieldToUTF8String(block.FirstName) +
+ " " + Helpers.FieldToUTF8String(block.LastName);
+ }
+
+ OnAvatarNames(names);
+ }
+ }
+
+ ///
+ /// Handle incoming friend notifications
+ ///
+ ///
+ ///
+ private void FriendNotificationHandler(Packet packet, Simulator simulator)
+ {
+ if (OnFriendNotification != null)
+ {
+ if (packet.Type == PacketType.OnlineNotification)
+ {
+ // If the agent is online
+ foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock)
+ OnFriendNotification(block.AgentID, true);
+ }
+ else if (packet.Type == PacketType.OfflineNotification)
+ {
+ // If the agent is offline
+ foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock)
+ OnFriendNotification(block.AgentID, true);
+ }
+ }
+ }
+
+ ///
+ /// Handles incoming avatar statistics, such as ratings
+ ///
+ ///
+ ///
+ private void AvatarStatisticsHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarStatistics != null)
+ {
+ AvatarStatisticsReplyPacket asr = (AvatarStatisticsReplyPacket)packet;
+ Avatar.Statistics stats = new Avatar.Statistics();
+
+ foreach (AvatarStatisticsReplyPacket.StatisticsDataBlock b in asr.StatisticsData)
+ {
+ string n = Helpers.FieldToUTF8String(b.Name);
+
+ switch (n)
+ {
+ case "Behavior":
+ stats.BehaviorPositive = b.Positive;
+ stats.BehaviorNegative = b.Negative;
+ break;
+ case "Appearance":
+ stats.AppearancePositive = b.Positive;
+ stats.AppearanceNegative = b.Negative;
+ break;
+ case "Building":
+ stats.AppearancePositive = b.Positive;
+ stats.AppearanceNegative = b.Negative;
+ break;
+ case "Given":
+ stats.GivenPositive = b.Positive;
+ stats.GivenNegative = b.Negative;
+ break;
+ default:
+ Client.Log("Got an AvatarStatistics block with the name " + n, Helpers.LogLevel.Warning);
+ break;
+ }
+ }
+
+ OnAvatarStatistics(asr.AvatarData.AvatarID, stats);
+ }
+ }
+
+ ///
+ /// Process incoming avatar properties (profile data)
+ ///
+ ///
+ ///
+ private void AvatarPropertiesHandler(Packet packet, Simulator sim)
+ {
+ if (OnAvatarProperties != null)
+ {
+ AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet;
+ Avatar.AvatarProperties properties = new Avatar.AvatarProperties();
+
+ properties.ProfileImage = reply.PropertiesData.ImageID;
+ properties.FirstLifeImage = reply.PropertiesData.FLImageID;
+ properties.Partner = reply.PropertiesData.PartnerID;
+ properties.AboutText = Helpers.FieldToUTF8String(reply.PropertiesData.AboutText);
+ properties.FirstLifeText = Helpers.FieldToUTF8String(reply.PropertiesData.FLAboutText);
+ properties.BornOn = Helpers.FieldToUTF8String(reply.PropertiesData.BornOn);
+ properties.CharterMember = Helpers.FieldToUTF8String(reply.PropertiesData.CharterMember);
+ // FIXME: These have been converted in to a Flags field, build an enum and fix this!
+ //properties.AllowPublish = reply.PropertiesData.AllowPublish;
+ //properties.MaturePublish = reply.PropertiesData.MaturePublish;
+ //properties.Identified = reply.PropertiesData.Identified;
+ //properties.Transacted = reply.PropertiesData.Transacted;
+ properties.ProfileURL = Helpers.FieldToUTF8String(reply.PropertiesData.ProfileURL);
+
+ OnAvatarProperties(reply.AgentData.AvatarID, properties);
+ }
+ }
+
+ ///
+ /// Process incoming Avatar Interests information
+ ///
+ private void AvatarInterestsHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarInterests != null)
+ {
+ AvatarInterestsReplyPacket airp = (AvatarInterestsReplyPacket)packet;
+ Avatar.Interests interests = new Avatar.Interests();
+
+ interests.WantToMask = airp.PropertiesData.WantToMask;
+ interests.WantToText = Helpers.FieldToUTF8String(airp.PropertiesData.WantToText);
+ interests.SkillsMask = airp.PropertiesData.SkillsMask;
+ interests.SkillsText = Helpers.FieldToUTF8String(airp.PropertiesData.SkillsText);
+ interests.LanguagesText = Helpers.FieldToUTF8String(airp.PropertiesData.LanguagesText);
+
+ OnAvatarInterests(airp.AgentData.AvatarID, interests);
+ }
+ }
+
+ private void AvatarGroupsHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarGroups != null)
+ {
+ AvatarGroupsReplyPacket groups = (AvatarGroupsReplyPacket)packet;
+
+ // FIXME: Build a little struct to represent the groups.GroupData blocks so we keep
+ // libsecondlife.Packets abstracted away
+ OnAvatarGroups(groups.AgentData.AvatarID, groups.GroupData);
+ }
+ }
+
+ private void AvatarPickerReplyHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarNameSearch != null)
+ {
+ AvatarPickerReplyPacket reply = (AvatarPickerReplyPacket)packet;
+ Dictionary avatars = new Dictionary();
+
+ foreach (AvatarPickerReplyPacket.DataBlock block in reply.Data)
+ {
+ avatars[block.AvatarID] = Helpers.FieldToUTF8String(block.FirstName) +
+ " " + Helpers.FieldToUTF8String(block.LastName);
+ }
+
+ OnAvatarNameSearch(reply.AgentData.QueryID, avatars);
+ }
+ }
+
+ ///
+ /// Process an incoming effect
+ ///
+ private void ViewerEffectHandler(Packet packet, Simulator simulator)
+ {
+ ViewerEffectPacket effect = (ViewerEffectPacket)packet;
+
+ foreach (ViewerEffectPacket.EffectBlock block in effect.Effect)
+ {
+ MainAvatar.EffectType type;
+
+ try
+ {
+ type = (MainAvatar.EffectType)block.Type;
+ }
+ catch (Exception)
+ {
+ Client.Log("Received a ViewerEffect block with an unknown type " + block.Type,
+ Helpers.LogLevel.Warning);
+ continue;
+ }
+
+ //LLColor color;
+ //if (block.Color.Length == 4)
+ //{
+ // color = new LLColor(block.Color, 0);
+ //}
+ //else
+ //{
+ // Client.Log("Received a ViewerEffect.EffectBlock.Color array with " + block.Color.Length + " bytes",
+ // Helpers.LogLevel.Warning);
+ // color = new LLColor();
+ //}
+
+ // Each ViewerEffect type uses it's own custom binary format for additional data. Fun eh?
+ switch (type)
+ {
+ case MainAvatar.EffectType.Text:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Icon:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Connector:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.FlexibleObject:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.AnimalControls:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.AnimationObject:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Cloth:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Beam:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Glow:
+ Client.Log("Received a Glow ViewerEffect which is not implemented yet",
+ Helpers.LogLevel.Warning);
+ break;
+ case MainAvatar.EffectType.Point:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Trail:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Sphere:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Spiral:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.Edit:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.LookAt:
+ //Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!");
+ break;
+ case MainAvatar.EffectType.PointAt:
+ if (block.TypeData.Length == 57)
+ {
+ LLUUID sourceAvatar = new LLUUID(block.TypeData, 0);
+ LLUUID targetObject = new LLUUID(block.TypeData, 16);
+ LLVector3d targetPos = new LLVector3d(block.TypeData, 32);
+ MainAvatar.PointAtType pointAt;
+ try
+ {
+ pointAt = (MainAvatar.PointAtType)block.TypeData[56];
+ }
+ catch (Exception)
+ {
+ Client.Log("Unrecognized PointAtType " + block.TypeData[56], Helpers.LogLevel.Warning);
+ pointAt = MainAvatar.PointAtType.Clear;
+ }
+
+ // TODO: Create a OnAvatarPointAt event and call it here
+ }
+ else
+ {
+ Client.Log("Received a PointAt ViewerEffect with an incorrect TypeData size of " +
+ block.TypeData.Length + " bytes", Helpers.LogLevel.Warning);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libsecondlife-cs/BakeLayer.cs b/libsecondlife-cs/BakeLayer.cs
index 7a3a497a..70ea4386 100644
--- a/libsecondlife-cs/BakeLayer.cs
+++ b/libsecondlife-cs/BakeLayer.cs
@@ -1,230 +1,230 @@
-/*
- * Copyright (c) 2007, 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.Drawing;
-using System.Drawing.Imaging;
-using System.Collections.Generic;
-using System.IO;
-using System.Reflection;
-
-namespace libsecondlife
-{
- ///
- /// A set of textures that are layered on top of each other and "baked"
- /// in to a single texture, for avatar appearances
- ///
- public class BakeLayer
- {
- public enum BakeOrder
- {
- Unknown = -1,
- HeadBodypaint = 0,
- Hair,
- EyesIris,
- UpperBodypaint,
- UpperUndershirt,
- UpperShirt,
- UpperJacket,
- LowerBodypaint,
- LowerUnderpants,
- LowerSocks,
- LowerShoes,
- LowerPants,
- LowerJacket,
- Skirt
- }
-
- /// Maximum number of wearables for any baked layer
- public const int WEARABLES_PER_LAYER = 7;
-
- /// Final compressed JPEG2000 data
- public byte[] FinalData = new byte[0];
- /// Whether this bake is complete or not
- public bool Finished = false;
-
- /// Reference to the SecondLife client
- protected SecondLife Client;
- /// Total number of textures in this bake
- protected int TotalLayers;
- /// Appearance parameters the drive the baking process
- protected Dictionary ParamValues;
- /// GDI+ image that textures are composited to
- protected Bitmap Scratchpad;
- /// List of textures sorted by their baking order
- protected SortedList Textures = new SortedList(WEARABLES_PER_LAYER);
- /// Width of the final baked image and scratchpad
- protected int BakeWidth = 512;
- /// Height of the final baked image and scratchpad
- protected int BakeHeight = 512;
-
-
- private Assembly assembly = null;
-
-
- ///
- /// Default constructor
- ///
- /// Reference to the SecondLife client
- /// Total number of layers this bake set is
- /// composed of
- /// Appearance parameters the drive the
- /// baking process
- public BakeLayer(SecondLife client, int totalLayers, Dictionary paramValues)
- {
- Client = client;
- TotalLayers = totalLayers;
- ParamValues = paramValues;
- }
-
- ///
- /// Default constructor
- ///
- /// Reference to the SecondLife client
- /// Total number of layers this bake set is
- /// composed of
- /// Appearance parameters the drive the
- /// baking process
- /// Width of the final baked image
- /// Height of the final baked image
- public BakeLayer(SecondLife client, int totalLayers, Dictionary paramValues, int width, int height)
- {
- Client = client;
- TotalLayers = totalLayers;
- ParamValues = paramValues;
- BakeWidth = width;
- BakeHeight = height;
- }
-
- ///
- /// Adds an image to this baking layer and potentially processes it, or
- /// stores it for processing later
- ///
- /// The baking layer index of the image to be added
- /// JPEG2000 compressed image to be added to the
- /// baking layer
- /// True if this layer is completely baked and JPEG2000 data
- /// is available, otherwise false
- public bool AddImage(BakeOrder index, byte[] jp2data)
- {
- lock (Textures)
- {
- Textures.Add(index, jp2data);
-
- if (Textures.Count == TotalLayers)
- {
- // All of the layers are in place, we can bake
- Bake();
- Finished = true;
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// Create the various dynamic alpha masks, apply them to the affected
- /// textures, and composite all of the textures in to the final scratch
- /// pad
- ///
- protected void Bake()
- {
- if (TotalLayers == 1)
- {
- // FIXME: Create a properly formatted JP2 bake (5 comps)
- }
- else
- {
- Client.Log("Too many layers for the null baking code!", Helpers.LogLevel.Error);
- }
- }
-
- private StreamReader GetResource(string resourceName)
- {
- if (assembly == null) assembly = Assembly.GetExecutingAssembly();
-
- return new StreamReader(assembly.GetManifestResourceStream(String.Format("libsecondlife.Resources.{0}",
- resourceName)));
- }
- }
-
- ///
- ///
- ///
- public class UpperBakeLayer : BakeLayer
- {
- ///
- /// Default constructor
- ///
- /// Reference to the SecondLife client
- /// Total number of layers this bake set is
- /// composed of
- /// Appearance parameters the drive the
- /// baking process
- public UpperBakeLayer(SecondLife client, int totalLayers, Dictionary paramValues)
- : base(client, totalLayers, paramValues)
- {
- }
-
- ///
- ///
- ///
- protected new void Bake()
- {
- // FIXME: Iterate through each texture, generate the alpha masks and apply them,
- // and combine the masked texture in to the final scratch pad
- }
- }
-
- ///
- ///
- ///
- public class LowerBakeLayer : BakeLayer
- {
- ///
- /// Default constructor
- ///
- /// Reference to the SecondLife client
- /// Total number of layers this bake set is
- /// composed of
- /// Appearance parameters the drive the
- /// baking process
- public LowerBakeLayer(SecondLife client, int totalLayers, Dictionary paramValues)
- : base(client, totalLayers, paramValues)
- {
- }
-
- ///
- ///
- ///
- protected new void Bake()
- {
- // FIXME: Iterate through each texture, generate the alpha masks and apply them,
- // and combine the masked texture in to the final scratch pad
- }
- }
-}
+/*
+ * Copyright (c) 2007, 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.Drawing;
+using System.Drawing.Imaging;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+namespace libsecondlife
+{
+ ///
+ /// A set of textures that are layered on top of each other and "baked"
+ /// in to a single texture, for avatar appearances
+ ///
+ public class BakeLayer
+ {
+ public enum BakeOrder
+ {
+ Unknown = -1,
+ HeadBodypaint = 0,
+ Hair,
+ EyesIris,
+ UpperBodypaint,
+ UpperUndershirt,
+ UpperShirt,
+ UpperJacket,
+ LowerBodypaint,
+ LowerUnderpants,
+ LowerSocks,
+ LowerShoes,
+ LowerPants,
+ LowerJacket,
+ Skirt
+ }
+
+ /// Maximum number of wearables for any baked layer
+ public const int WEARABLES_PER_LAYER = 7;
+
+ /// Final compressed JPEG2000 data
+ public byte[] FinalData = new byte[0];
+ /// Whether this bake is complete or not
+ public bool Finished = false;
+
+ /// Reference to the SecondLife client
+ protected SecondLife Client;
+ /// Total number of textures in this bake
+ protected int TotalLayers;
+ /// Appearance parameters the drive the baking process
+ protected Dictionary ParamValues;
+ /// GDI+ image that textures are composited to
+ protected Bitmap Scratchpad;
+ /// List of textures sorted by their baking order
+ protected SortedList Textures = new SortedList(WEARABLES_PER_LAYER);
+ /// Width of the final baked image and scratchpad
+ protected int BakeWidth = 512;
+ /// Height of the final baked image and scratchpad
+ protected int BakeHeight = 512;
+
+
+ private Assembly assembly = null;
+
+
+ ///
+ /// Default constructor
+ ///
+ /// Reference to the SecondLife client
+ /// Total number of layers this bake set is
+ /// composed of
+ /// Appearance parameters the drive the
+ /// baking process
+ public BakeLayer(SecondLife client, int totalLayers, Dictionary paramValues)
+ {
+ Client = client;
+ TotalLayers = totalLayers;
+ ParamValues = paramValues;
+ }
+
+ ///
+ /// Default constructor
+ ///
+ /// Reference to the SecondLife client
+ /// Total number of layers this bake set is
+ /// composed of
+ /// Appearance parameters the drive the
+ /// baking process
+ /// Width of the final baked image
+ /// Height of the final baked image
+ public BakeLayer(SecondLife client, int totalLayers, Dictionary paramValues, int width, int height)
+ {
+ Client = client;
+ TotalLayers = totalLayers;
+ ParamValues = paramValues;
+ BakeWidth = width;
+ BakeHeight = height;
+ }
+
+ ///
+ /// Adds an image to this baking layer and potentially processes it, or
+ /// stores it for processing later
+ ///
+ /// The baking layer index of the image to be added
+ /// JPEG2000 compressed image to be added to the
+ /// baking layer
+ /// True if this layer is completely baked and JPEG2000 data
+ /// is available, otherwise false
+ public bool AddImage(BakeOrder index, byte[] jp2data)
+ {
+ lock (Textures)
+ {
+ Textures.Add(index, jp2data);
+
+ if (Textures.Count == TotalLayers)
+ {
+ // All of the layers are in place, we can bake
+ Bake();
+ Finished = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Create the various dynamic alpha masks, apply them to the affected
+ /// textures, and composite all of the textures in to the final scratch
+ /// pad
+ ///
+ protected void Bake()
+ {
+ if (TotalLayers == 1)
+ {
+ // FIXME: Create a properly formatted JP2 bake (5 comps)
+ }
+ else
+ {
+ Client.Log("Too many layers for the null baking code!", Helpers.LogLevel.Error);
+ }
+ }
+
+ private StreamReader GetResource(string resourceName)
+ {
+ if (assembly == null) assembly = Assembly.GetExecutingAssembly();
+
+ return new StreamReader(assembly.GetManifestResourceStream(String.Format("libsecondlife.Resources.{0}",
+ resourceName)));
+ }
+ }
+
+ ///
+ ///
+ ///
+ public class UpperBakeLayer : BakeLayer
+ {
+ ///
+ /// Default constructor
+ ///
+ /// Reference to the SecondLife client
+ /// Total number of layers this bake set is
+ /// composed of
+ /// Appearance parameters the drive the
+ /// baking process
+ public UpperBakeLayer(SecondLife client, int totalLayers, Dictionary paramValues)
+ : base(client, totalLayers, paramValues)
+ {
+ }
+
+ ///
+ ///
+ ///
+ protected new void Bake()
+ {
+ // FIXME: Iterate through each texture, generate the alpha masks and apply them,
+ // and combine the masked texture in to the final scratch pad
+ }
+ }
+
+ ///
+ ///
+ ///
+ public class LowerBakeLayer : BakeLayer
+ {
+ ///
+ /// Default constructor
+ ///
+ /// Reference to the SecondLife client
+ /// Total number of layers this bake set is
+ /// composed of
+ /// Appearance parameters the drive the
+ /// baking process
+ public LowerBakeLayer(SecondLife client, int totalLayers, Dictionary paramValues)
+ : base(client, totalLayers, paramValues)
+ {
+ }
+
+ ///
+ ///
+ ///
+ protected new void Bake()
+ {
+ // FIXME: Iterate through each texture, generate the alpha masks and apply them,
+ // and combine the masked texture in to the final scratch pad
+ }
+ }
+}
diff --git a/libsecondlife-cs/BitPack.cs b/libsecondlife-cs/BitPack.cs
index a11d6e69..404a37e1 100644
--- a/libsecondlife-cs/BitPack.cs
+++ b/libsecondlife-cs/BitPack.cs
@@ -1,275 +1,275 @@
-/*
- * Copyright (c) 2007, 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;
-
-namespace libsecondlife
-{
- ///
- /// Wrapper around a byte array that allows bit to be packed and unpacked
- /// one at a time or by a variable amount. Useful for very tightly packed
- /// data like LayerData packets
- ///
- public class BitPack
- {
- ///
- public byte[] Data;
-
- ///
- public int BytePos
- {
- get
- {
- if (bytePos != 0 && bitPos == 0)
- return bytePos - 1;
- else
- return bytePos;
- }
- }
-
- ///
- public int BitPos { get { return bitPos; } }
-
-
- private const int MAX_BITS = 8;
-
- private int bytePos;
- private int bitPos;
-
-
- ///
- /// Default constructor, initialize the bit packer / bit unpacker
- /// with a byte array and starting position
- ///
- /// Byte array to pack bits in to or unpack from
- /// Starting position in the byte array
- public BitPack(byte[] data, int pos)
- {
- Data = data;
- bytePos = pos;
- }
-
- ///
- /// Pack a floating point value in to the data
- ///
- /// Floating point value to pack
- public void PackFloat(float data)
- {
- byte[] input = BitConverter.GetBytes(data);
- PackBitArray(input, 32);
- }
-
- ///
- /// Pack part or all of an integer in to the data
- ///
- /// Integer containing the data to pack
- /// Number of bits of the integer to pack
- public void PackBits(int data, int totalCount)
- {
- byte[] input = BitConverter.GetBytes(data);
- PackBitArray(input, totalCount);
- }
-
- ///
- /// Unpacking a floating point value from the data
- ///
- /// Unpacked floating point value
- public float UnpackFloat()
- {
- byte[] output = UnpackBitsArray(32);
-
- if (!BitConverter.IsLittleEndian) Array.Reverse(output);
- return BitConverter.ToSingle(output, 0);
- }
-
- ///
- /// Unpack a variable number of bits from the data in to integer format
- ///
- /// Number of bits to unpack
- /// An integer containing the unpacked bits
- /// This function is only useful up to 32 bits
- public int UnpackBits(int totalCount)
- {
- byte[] output = UnpackBitsArray(totalCount);
-
- if (!BitConverter.IsLittleEndian) Array.Reverse(output);
- return BitConverter.ToInt32(output, 0);
- }
-
- ///
- /// Unpack a variable number of bits from the data in to unsigned
- /// integer format
- ///
- /// Number of bits to unpack
- /// An unsigned integer containing the unpacked bits
- /// This function is only useful up to 32 bits
- public uint UnpackUBits(int totalCount)
- {
- byte[] output = UnpackBitsArray(totalCount);
-
- if (!BitConverter.IsLittleEndian) Array.Reverse(output);
- return BitConverter.ToUInt32(output, 0);
- }
-
- public byte UnpackByte()
- {
- byte[] output = UnpackBitsArray(8);
- return output[0];
- }
-
- public float UnpackFixed(bool signed, int intBits, int fracBits)
- {
- int minVal;
- int maxVal;
- int unsignedBits = intBits + fracBits;
- int totalBits = unsignedBits;
- float fixedVal;
-
- if (signed)
- {
- totalBits++;
-
- minVal = 1 << intBits;
- minVal *= -1;
- }
- maxVal = 1 << intBits;
-
- if (totalBits <= 8)
- fixedVal = (float)UnpackByte();
- else if (totalBits <= 16)
- fixedVal = (float)UnpackUBits(16);
- else if (totalBits <= 31)
- fixedVal = (float)UnpackUBits(32);
- else
- return 0.0f;
-
- fixedVal /= (float)(1 << fracBits);
-
- if (signed) fixedVal -= (float)maxVal;
-
- return fixedVal;
- }
-
- public LLUUID UnpackUUID()
- {
- if (bitPos != 0) return LLUUID.Zero;
-
- LLUUID val = new LLUUID(Data, bytePos);
- bytePos += 16;
- return val;
- }
-
- private void PackBitArray(byte[] data, int totalCount)
- {
- int count = 0;
- int curBytePos = 0;
- int curBitPos = 0;
-
- while (totalCount > 0)
- {
- if (totalCount > MAX_BITS)
- {
- count = MAX_BITS;
- totalCount -= MAX_BITS;
- }
- else
- {
- count = totalCount;
- totalCount = 0;
- }
-
- while (count > 0)
- {
- if ((data[curBytePos] & (0x01 << (count - 1))) != 0)
- Data[bytePos] |= (byte)(0x80 >> bitPos);
-
- --count;
- ++bitPos;
- ++curBitPos;
-
- if (bitPos >= MAX_BITS)
- {
- bitPos = 0;
- ++bytePos;
- }
- if (curBitPos >= MAX_BITS)
- {
- curBitPos = 0;
- ++curBytePos;
- }
- }
- }
- }
-
- private byte[] UnpackBitsArray(int totalCount)
- {
- int count = 0;
- byte[] output = new byte[4];
- int curBytePos = 0;
- int curBitPos = 0;
-
- while (totalCount > 0)
- {
- if (totalCount > MAX_BITS)
- {
- count = MAX_BITS;
- totalCount -= MAX_BITS;
- }
- else
- {
- count = totalCount;
- totalCount = 0;
- }
-
- while (count > 0)
- {
- // Shift the previous bits
- output[curBytePos] <<= 1;
-
- // Grab one bit
- if ((Data[bytePos] & (0x80 >> bitPos++)) != 0)
- ++output[curBytePos];
-
- --count;
- ++curBitPos;
-
- if (bitPos >= MAX_BITS)
- {
- bitPos = 0;
- ++bytePos;
- }
- if (curBitPos >= MAX_BITS)
- {
- curBitPos = 0;
- ++curBytePos;
- }
- }
- }
-
- return output;
- }
- }
-}
+/*
+ * Copyright (c) 2007, 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;
+
+namespace libsecondlife
+{
+ ///
+ /// Wrapper around a byte array that allows bit to be packed and unpacked
+ /// one at a time or by a variable amount. Useful for very tightly packed
+ /// data like LayerData packets
+ ///
+ public class BitPack
+ {
+ ///
+ public byte[] Data;
+
+ ///
+ public int BytePos
+ {
+ get
+ {
+ if (bytePos != 0 && bitPos == 0)
+ return bytePos - 1;
+ else
+ return bytePos;
+ }
+ }
+
+ ///
+ public int BitPos { get { return bitPos; } }
+
+
+ private const int MAX_BITS = 8;
+
+ private int bytePos;
+ private int bitPos;
+
+
+ ///
+ /// Default constructor, initialize the bit packer / bit unpacker
+ /// with a byte array and starting position
+ ///
+ /// Byte array to pack bits in to or unpack from
+ /// Starting position in the byte array
+ public BitPack(byte[] data, int pos)
+ {
+ Data = data;
+ bytePos = pos;
+ }
+
+ ///
+ /// Pack a floating point value in to the data
+ ///
+ /// Floating point value to pack
+ public void PackFloat(float data)
+ {
+ byte[] input = BitConverter.GetBytes(data);
+ PackBitArray(input, 32);
+ }
+
+ ///
+ /// Pack part or all of an integer in to the data
+ ///
+ /// Integer containing the data to pack
+ /// Number of bits of the integer to pack
+ public void PackBits(int data, int totalCount)
+ {
+ byte[] input = BitConverter.GetBytes(data);
+ PackBitArray(input, totalCount);
+ }
+
+ ///
+ /// Unpacking a floating point value from the data
+ ///
+ /// Unpacked floating point value
+ public float UnpackFloat()
+ {
+ byte[] output = UnpackBitsArray(32);
+
+ if (!BitConverter.IsLittleEndian) Array.Reverse(output);
+ return BitConverter.ToSingle(output, 0);
+ }
+
+ ///
+ /// Unpack a variable number of bits from the data in to integer format
+ ///
+ /// Number of bits to unpack
+ /// An integer containing the unpacked bits
+ /// This function is only useful up to 32 bits
+ public int UnpackBits(int totalCount)
+ {
+ byte[] output = UnpackBitsArray(totalCount);
+
+ if (!BitConverter.IsLittleEndian) Array.Reverse(output);
+ return BitConverter.ToInt32(output, 0);
+ }
+
+ ///
+ /// Unpack a variable number of bits from the data in to unsigned
+ /// integer format
+ ///
+ /// Number of bits to unpack
+ /// An unsigned integer containing the unpacked bits
+ /// This function is only useful up to 32 bits
+ public uint UnpackUBits(int totalCount)
+ {
+ byte[] output = UnpackBitsArray(totalCount);
+
+ if (!BitConverter.IsLittleEndian) Array.Reverse(output);
+ return BitConverter.ToUInt32(output, 0);
+ }
+
+ public byte UnpackByte()
+ {
+ byte[] output = UnpackBitsArray(8);
+ return output[0];
+ }
+
+ public float UnpackFixed(bool signed, int intBits, int fracBits)
+ {
+ int minVal;
+ int maxVal;
+ int unsignedBits = intBits + fracBits;
+ int totalBits = unsignedBits;
+ float fixedVal;
+
+ if (signed)
+ {
+ totalBits++;
+
+ minVal = 1 << intBits;
+ minVal *= -1;
+ }
+ maxVal = 1 << intBits;
+
+ if (totalBits <= 8)
+ fixedVal = (float)UnpackByte();
+ else if (totalBits <= 16)
+ fixedVal = (float)UnpackUBits(16);
+ else if (totalBits <= 31)
+ fixedVal = (float)UnpackUBits(32);
+ else
+ return 0.0f;
+
+ fixedVal /= (float)(1 << fracBits);
+
+ if (signed) fixedVal -= (float)maxVal;
+
+ return fixedVal;
+ }
+
+ public LLUUID UnpackUUID()
+ {
+ if (bitPos != 0) return LLUUID.Zero;
+
+ LLUUID val = new LLUUID(Data, bytePos);
+ bytePos += 16;
+ return val;
+ }
+
+ private void PackBitArray(byte[] data, int totalCount)
+ {
+ int count = 0;
+ int curBytePos = 0;
+ int curBitPos = 0;
+
+ while (totalCount > 0)
+ {
+ if (totalCount > MAX_BITS)
+ {
+ count = MAX_BITS;
+ totalCount -= MAX_BITS;
+ }
+ else
+ {
+ count = totalCount;
+ totalCount = 0;
+ }
+
+ while (count > 0)
+ {
+ if ((data[curBytePos] & (0x01 << (count - 1))) != 0)
+ Data[bytePos] |= (byte)(0x80 >> bitPos);
+
+ --count;
+ ++bitPos;
+ ++curBitPos;
+
+ if (bitPos >= MAX_BITS)
+ {
+ bitPos = 0;
+ ++bytePos;
+ }
+ if (curBitPos >= MAX_BITS)
+ {
+ curBitPos = 0;
+ ++curBytePos;
+ }
+ }
+ }
+ }
+
+ private byte[] UnpackBitsArray(int totalCount)
+ {
+ int count = 0;
+ byte[] output = new byte[4];
+ int curBytePos = 0;
+ int curBitPos = 0;
+
+ while (totalCount > 0)
+ {
+ if (totalCount > MAX_BITS)
+ {
+ count = MAX_BITS;
+ totalCount -= MAX_BITS;
+ }
+ else
+ {
+ count = totalCount;
+ totalCount = 0;
+ }
+
+ while (count > 0)
+ {
+ // Shift the previous bits
+ output[curBytePos] <<= 1;
+
+ // Grab one bit
+ if ((Data[bytePos] & (0x80 >> bitPos++)) != 0)
+ ++output[curBytePos];
+
+ --count;
+ ++curBitPos;
+
+ if (bitPos >= MAX_BITS)
+ {
+ bitPos = 0;
+ ++bytePos;
+ }
+ if (curBitPos >= MAX_BITS)
+ {
+ curBitPos = 0;
+ ++curBytePos;
+ }
+ }
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/libsecondlife-cs/Caps.cs b/libsecondlife-cs/Caps.cs
index 45bbe75c..4a76bf3c 100644
--- a/libsecondlife-cs/Caps.cs
+++ b/libsecondlife-cs/Caps.cs
@@ -1,232 +1,232 @@
-/*
- * Copyright (c) 2007, 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.Threading;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Net;
-using System.IO;
-
-namespace libsecondlife
-{
- ///
- /// Capabilities is the name of the bi-directional HTTP REST protocol that
- /// Second Life uses to communicate transactions such as teleporting or
- /// asset transfers
- ///
- public class Caps
- {
- ///
- /// Triggered when an event is received via the EventQueueGet capability;
- ///
- ///
- ///
- public delegate void EventQueueCallback(string message, object body);
-
- /// Reference to the SecondLife client this system is connected to
- public SecondLife Client;
- /// Reference to the simulator this system is connected to
- public Simulator Simulator;
-
- ///
- public string SeedCapsURI { get { return Seedcaps; } }
-
- internal bool Dead = false;
- internal string Seedcaps;
-
- private StringDictionary Capabilities = new StringDictionary();
- private Thread CapsThread;
- private string EventQueueCap = String.Empty;
- private WebRequest EventQueueRequest = null;
-
- ///
- /// Default constructor
- ///
- ///
- ///
- ///
- ///
- internal Caps(SecondLife client, Simulator simulator, string seedcaps)
- {
- Client = client;
- Simulator = simulator;
- Seedcaps = seedcaps;
-
- CapsThread = new Thread(new ThreadStart(Run));
- CapsThread.Start();
- }
-
- ///
- ///
- ///
- ///
- internal void Disconnect(bool immediate)
- {
- Dead = true;
-
- if (immediate && EventQueueRequest != null)
- EventQueueRequest.Abort();
- }
-
- private void Run()
- {
- byte[] buffer = null;
- ArrayList req = new ArrayList();
- req.Add("MapLayer");
- req.Add("MapLayerGod");
- req.Add("NewAgentInventory");
- req.Add("EventQueueGet");
- byte[] data = LLSD.LLSDSerialize(req);
-
- MakeRequest:
-
- try
- {
- WebRequest request = WebRequest.Create(Seedcaps);
- request.Method = "POST";
- request.ContentLength = data.Length;
-
- Stream reqStream = request.GetRequestStream();
- reqStream.Write(data, 0, data.Length);
- reqStream.Close();
-
- WebResponse response = request.GetResponse();
- BinaryReader reader = new BinaryReader(response.GetResponseStream());
- buffer = reader.ReadBytes((int)response.ContentLength);
- response.Close();
- }
- catch (WebException e)
- {
- // Thank you .NET creators for giving us such a piss-poor way of checking for HTTP errors
- if (e.Message.Contains("404"))
- {
- // This capability no longer exists, disable it
- Disconnect(true);
- return;
- }
-
- Client.Log("CAPS initialization error: " + e.Message + ", retrying", Helpers.LogLevel.Warning);
- goto MakeRequest;
- }
-
- Hashtable resp = (Hashtable)LLSD.LLSDDeserialize(buffer);
-
- foreach (string cap in resp.Keys)
- {
- Client.DebugLog(String.Format("Got cap {0}: {1}", cap, (string)resp[cap]));
- Capabilities[cap] = (string)resp[cap];
- }
-
- if (Capabilities.ContainsKey("EventQueueGet"))
- {
- EventQueueCap = Capabilities["EventQueueGet"];
- Client.Log("Running event queue", Helpers.LogLevel.Info);
- EventQueueHandler(null);
- }
- }
-
- private void EventQueueHandler(IAsyncResult result)
- {
- byte[] buffer = null;
- long ack = 0;
-
- // Special handler for the first request
- if (result == null) goto MakeRequest;
-
- try
- {
- HttpWebResponse response = (HttpWebResponse)EventQueueRequest.EndGetResponse(result);
- BinaryReader reader = new BinaryReader(response.GetResponseStream());
- buffer = reader.ReadBytes((int)response.ContentLength);
- response.Close();
- }
- catch (WebException e)
- {
- string extstring=e.Message;
- if (e.Message.IndexOf("502") < 0)
- Client.DebugLog("EventQueue response: " + e.Message);
- }
-
- if (buffer != null)
- {
- lock (Client.Network.EventQueueCallbacks)
- {
- Hashtable resp = (Hashtable)LLSD.LLSDDeserialize(buffer);
- ArrayList events = (ArrayList)resp["events"];
- ack = (long)resp["id"];
-
- foreach (Hashtable evt in events)
- {
- string msg = (string)evt["message"];
- object body = (object)evt["body"];
-
- Client.DebugLog("Event " + msg + ":" + Environment.NewLine + LLSD.LLSDDump(body, 0));
-
- for (int i = 0; i < Client.Network.EventQueueCallbacks.Count; i++)
- {
- try { Client.Network.EventQueueCallbacks[i](msg, body); }
- catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
- }
- }
- }
- }
-
- MakeRequest:
-
- // Make a new request
- Hashtable req = new Hashtable();
- if (ack != 0) req["ack"] = ack;
- else req["ack"] = null;
- req["done"] = Dead;
-
- byte[] data = LLSD.LLSDSerialize(req);
-
- try
- {
- EventQueueRequest = WebRequest.Create(EventQueueCap);
- EventQueueRequest.Method = "POST";
- EventQueueRequest.ContentLength = data.Length;
-
- Stream reqStream = EventQueueRequest.GetRequestStream();
- reqStream.Write(data, 0, data.Length);
- reqStream.Close();
-
- if (!Dead)
- EventQueueRequest.BeginGetResponse(new AsyncCallback(EventQueueHandler), EventQueueRequest);
- else
- EventQueueRequest.BeginGetResponse(null, EventQueueRequest);
- }
- catch (WebException e)
- {
- Client.DebugLog("EventQueue request: " + e.Message);
- // If the CAPS system is shutting down don't bother trying too hard
- if (!Dead) goto MakeRequest;
- }
- }
- }
-}
+/*
+ * Copyright (c) 2007, 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.Threading;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Net;
+using System.IO;
+
+namespace libsecondlife
+{
+ ///
+ /// Capabilities is the name of the bi-directional HTTP REST protocol that
+ /// Second Life uses to communicate transactions such as teleporting or
+ /// asset transfers
+ ///
+ public class Caps
+ {
+ ///
+ /// Triggered when an event is received via the EventQueueGet capability;
+ ///
+ ///
+ ///
+ public delegate void EventQueueCallback(string message, object body);
+
+ /// Reference to the SecondLife client this system is connected to
+ public SecondLife Client;
+ /// Reference to the simulator this system is connected to
+ public Simulator Simulator;
+
+ ///
+ public string SeedCapsURI { get { return Seedcaps; } }
+
+ internal bool Dead = false;
+ internal string Seedcaps;
+
+ private StringDictionary Capabilities = new StringDictionary();
+ private Thread CapsThread;
+ private string EventQueueCap = String.Empty;
+ private WebRequest EventQueueRequest = null;
+
+ ///
+ /// Default constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal Caps(SecondLife client, Simulator simulator, string seedcaps)
+ {
+ Client = client;
+ Simulator = simulator;
+ Seedcaps = seedcaps;
+
+ CapsThread = new Thread(new ThreadStart(Run));
+ CapsThread.Start();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ internal void Disconnect(bool immediate)
+ {
+ Dead = true;
+
+ if (immediate && EventQueueRequest != null)
+ EventQueueRequest.Abort();
+ }
+
+ private void Run()
+ {
+ byte[] buffer = null;
+ ArrayList req = new ArrayList();
+ req.Add("MapLayer");
+ req.Add("MapLayerGod");
+ req.Add("NewAgentInventory");
+ req.Add("EventQueueGet");
+ byte[] data = LLSD.LLSDSerialize(req);
+
+ MakeRequest:
+
+ try
+ {
+ WebRequest request = WebRequest.Create(Seedcaps);
+ request.Method = "POST";
+ request.ContentLength = data.Length;
+
+ Stream reqStream = request.GetRequestStream();
+ reqStream.Write(data, 0, data.Length);
+ reqStream.Close();
+
+ WebResponse response = request.GetResponse();
+ BinaryReader reader = new BinaryReader(response.GetResponseStream());
+ buffer = reader.ReadBytes((int)response.ContentLength);
+ response.Close();
+ }
+ catch (WebException e)
+ {
+ // Thank you .NET creators for giving us such a piss-poor way of checking for HTTP errors
+ if (e.Message.Contains("404"))
+ {
+ // This capability no longer exists, disable it
+ Disconnect(true);
+ return;
+ }
+
+ Client.Log("CAPS initialization error: " + e.Message + ", retrying", Helpers.LogLevel.Warning);
+ goto MakeRequest;
+ }
+
+ Hashtable resp = (Hashtable)LLSD.LLSDDeserialize(buffer);
+
+ foreach (string cap in resp.Keys)
+ {
+ Client.DebugLog(String.Format("Got cap {0}: {1}", cap, (string)resp[cap]));
+ Capabilities[cap] = (string)resp[cap];
+ }
+
+ if (Capabilities.ContainsKey("EventQueueGet"))
+ {
+ EventQueueCap = Capabilities["EventQueueGet"];
+ Client.Log("Running event queue", Helpers.LogLevel.Info);
+ EventQueueHandler(null);
+ }
+ }
+
+ private void EventQueueHandler(IAsyncResult result)
+ {
+ byte[] buffer = null;
+ long ack = 0;
+
+ // Special handler for the first request
+ if (result == null) goto MakeRequest;
+
+ try
+ {
+ HttpWebResponse response = (HttpWebResponse)EventQueueRequest.EndGetResponse(result);
+ BinaryReader reader = new BinaryReader(response.GetResponseStream());
+ buffer = reader.ReadBytes((int)response.ContentLength);
+ response.Close();
+ }
+ catch (WebException e)
+ {
+ string extstring=e.Message;
+ if (e.Message.IndexOf("502") < 0)
+ Client.DebugLog("EventQueue response: " + e.Message);
+ }
+
+ if (buffer != null)
+ {
+ lock (Client.Network.EventQueueCallbacks)
+ {
+ Hashtable resp = (Hashtable)LLSD.LLSDDeserialize(buffer);
+ ArrayList events = (ArrayList)resp["events"];
+ ack = (long)resp["id"];
+
+ foreach (Hashtable evt in events)
+ {
+ string msg = (string)evt["message"];
+ object body = (object)evt["body"];
+
+ Client.DebugLog("Event " + msg + ":" + Environment.NewLine + LLSD.LLSDDump(body, 0));
+
+ for (int i = 0; i < Client.Network.EventQueueCallbacks.Count; i++)
+ {
+ try { Client.Network.EventQueueCallbacks[i](msg, body); }
+ catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+ }
+ }
+
+ MakeRequest:
+
+ // Make a new request
+ Hashtable req = new Hashtable();
+ if (ack != 0) req["ack"] = ack;
+ else req["ack"] = null;
+ req["done"] = Dead;
+
+ byte[] data = LLSD.LLSDSerialize(req);
+
+ try
+ {
+ EventQueueRequest = WebRequest.Create(EventQueueCap);
+ EventQueueRequest.Method = "POST";
+ EventQueueRequest.ContentLength = data.Length;
+
+ Stream reqStream = EventQueueRequest.GetRequestStream();
+ reqStream.Write(data, 0, data.Length);
+ reqStream.Close();
+
+ if (!Dead)
+ EventQueueRequest.BeginGetResponse(new AsyncCallback(EventQueueHandler), EventQueueRequest);
+ else
+ EventQueueRequest.BeginGetResponse(null, EventQueueRequest);
+ }
+ catch (WebException e)
+ {
+ Client.DebugLog("EventQueue request: " + e.Message);
+ // If the CAPS system is shutting down don't bother trying too hard
+ if (!Dead) goto MakeRequest;
+ }
+ }
+ }
+}
diff --git a/libsecondlife-cs/DirectoryManager.cs b/libsecondlife-cs/DirectoryManager.cs
index 5a03eb4c..d05c4973 100644
--- a/libsecondlife-cs/DirectoryManager.cs
+++ b/libsecondlife-cs/DirectoryManager.cs
@@ -1,384 +1,384 @@
-/*
- * 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 System.Threading;
-using libsecondlife.Packets;
-
-namespace libsecondlife
-{
- ///
- ///
- ///
- public class DirectoryManager
- {
- ///
- /// The different categories a classified ad can be placed in
- ///
- public enum ClassifiedCategories
- {
- ///
- Any = 0,
- ///
- Shopping,
- ///
- LandRental,
- ///
- PropertyRental,
- ///
- SpecialAttraction,
- ///
- NewProducts,
- ///
- Employment,
- ///
- Wanted,
- ///
- Service,
- ///
- Personal
- }
-
- ///
- ///
- ///
- [Flags]
- public enum DirFindFlags
- {
- ///
- People = 1 << 0,
- ///
- Online = 1 << 1,
- ///
- [Obsolete]
- Places = 1 << 2,
- ///
- Events = 1 << 3,
- ///
- Groups = 1 << 4,
- ///
- DateEvents = 1 << 5,
- ///
- AgentOwned = 1 << 6,
- ///
- ForSale = 1 << 7,
- ///
- GroupOwned = 1 << 8,
- ///
- [Obsolete]
- Auction = 1 << 9,
- ///
- DwellSort = 1 << 10,
- ///
- PgSimsOnly = 1 << 11,
- ///
- PicturesOnly = 1 << 12,
- ///
- PgEventsOnly = 1 << 13,
- ///
- MatureSimsOnly = 1 << 14,
- ///
- SortAsc = 1 << 15,
- ///
- PricesSort = 1 << 16,
- ///
- PerMeterSort = 1 << 17,
- ///
- AreaSort = 1 << 18,
- ///
- NameSort = 1 << 19,
- ///
- LimitByPrice = 1 << 20,
- ///
- LimitByArea = 1 << 21
- }
-
- ///
- ///
- ///
- [Flags]
- public enum SearchTypeFlags
- {
- ///
- None = 0,
- ///
- Auction = 1 << 1,
- ///
- Newbie = 1 << 2,
- ///
- Mainland = 1 << 3,
- ///
- Estate = 1 << 4
- }
-
-
- ///
- /// A classified ad in Second Life
- ///
- public struct Classified
- {
- /// UUID for this ad, useful for looking up detailed
- /// information about it
- public LLUUID ID;
- /// The title of this classified ad
- public string Name;
- /// Unknown
- public byte Flags;
- /// Creation date of the ad
- public DateTime CreationDate;
- /// Expiration date of the ad
- public DateTime ExpirationDate;
- /// Price that was paid for this ad
- public int Price;
- }
-
- ///
- /// A parcel retrieved from the dataserver such as results from the
- /// "For-Sale" listings
- ///
- public struct DirectoryParcel
- {
- ///
- public LLUUID ID;
- ///
- public string Name;
- ///
- public int ActualArea;
- ///
- public int SalePrice;
- ///
- public bool Auction;
- ///
- public bool ForSale;
- ///
- public bool ReservedNewbie;
- }
-
- /*///
- public LLUUID OwnerID;
- ///
- public LLUUID SnapshotID;
- ///
- public ulong RegionHandle;
- ///
- public string SimName;
- ///
- public string Desc;
- ///
- public LLVector3 GlobalPosition;
- ///
- public LLVector3 SimPosition;
- ///
- public float Dwell;*/
-
-
- ///
- ///
- ///
- ///
- public delegate void ClassifiedReplyCallback(List classifieds);
- ///
- ///
- ///
- ///
- public delegate void DirLandReplyCallback(List dirParcels);
-
-
- ///
- ///
- ///
- public event ClassifiedReplyCallback OnClassifiedReply;
- ///
- ///
- ///
- public event DirLandReplyCallback OnDirLandReply;
-
-
- private SecondLife Client;
-
-
- public DirectoryManager(SecondLife client)
- {
- Client = client;
-
- Client.Network.RegisterCallback(PacketType.DirClassifiedReply, new NetworkManager.PacketCallback(DirClassifiedReplyHandler));
- Client.Network.RegisterCallback(PacketType.DirLandReply, new NetworkManager.PacketCallback(DirLandReplyHandler));
- }
-
- public LLUUID StartClassifiedSearch(string searchText, ClassifiedCategories categories, bool mature)
- {
- DirClassifiedQueryPacket query = new DirClassifiedQueryPacket();
- LLUUID queryID = LLUUID.Random();
-
- query.AgentData.AgentID = Client.Network.AgentID;
- query.AgentData.SessionID = Client.Network.SessionID;
- query.QueryData.Category = (uint)categories;
- query.QueryData.QueryFlags = (uint)(mature ? 0 : 2);
- query.QueryData.QueryID = queryID;
- query.QueryData.QueryText = Helpers.StringToField(searchText);
-
- Client.Network.SendPacket(query);
-
- return queryID;
- }
-
- ///
- /// Starts a search for land sales using the directory
- ///
- /// What type of land to search for. Auction,
- /// estate, mainland, "first land", etc
- /// A unique identifier that can identify packets associated
- /// with this query from other queries
- /// The OnDirLandReply event handler must be registered before
- /// calling this function. There is no way to determine how many
- /// results will be returned, or how many times the callback will be
- /// fired other than you won't get more than 100 total parcels from
- /// each query.
- public LLUUID StartLandSearch(SearchTypeFlags typeFlags)
- {
- return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort, typeFlags, 0, 0, 0);
- }
-
- ///
- /// Starts a search for land sales using the directory
- ///
- /// What type of land to search for. Auction,
- /// estate, mainland, "first land", etc
- /// Maximum price to search for
- /// Maximum area to search for
- /// Each request is limited to 100 parcels
- /// being returned. To get the first 100 parcels of a request use 0,
- /// from 100-199 use 1, 200-299 use 2, etc.
- /// A unique identifier that can identify packets associated
- /// with this query from other queries
- /// The OnDirLandReply event handler must be registered before
- /// calling this function. There is no way to determine how many
- /// results will be returned, or how many times the callback will be
- /// fired other than you won't get more than 100 total parcels from
- /// each query.
- public LLUUID StartLandSearch(SearchTypeFlags typeFlags, int priceLimit, int areaLimit, int queryStart)
- {
- return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort | DirFindFlags.LimitByPrice |
- DirFindFlags.LimitByArea, typeFlags, priceLimit, areaLimit, queryStart);
- }
-
- ///
- /// Starts a search for land sales using the directory
- ///
- /// A flags parameter that can modify the way
- /// search results are returned, for example changing the ordering of
- /// results or limiting based on price or area
- /// What type of land to search for. Auction,
- /// estate, mainland, "first land", etc
- /// Maximum price to search for, the
- /// DirFindFlags.LimitByPrice flag must be set
- /// Maximum area to search for, the
- /// DirFindFlags.LimitByArea flag must be set
- /// Each request is limited to 100 parcels
- /// being returned. To get the first 100 parcels of a request use 0,
- /// from 100-199 use 1, 200-299 use 2, etc.
- /// A unique identifier that can identify packets associated
- /// with this query from other queries
- /// The OnDirLandReply event handler must be registered before
- /// calling this function. There is no way to determine how many
- /// results will be returned, or how many times the callback will be
- /// fired other than you won't get more than 100 total parcels from
- /// each query.
- public LLUUID StartLandSearch(DirFindFlags findFlags, SearchTypeFlags typeFlags, int priceLimit,
- int areaLimit, int queryStart)
- {
- LLUUID queryID = LLUUID.Random();
-
- DirLandQueryPacket query = new DirLandQueryPacket();
- query.AgentData.AgentID = Client.Network.AgentID;
- query.AgentData.SessionID = Client.Network.SessionID;
- query.QueryData.Area = areaLimit;
- query.QueryData.Price = priceLimit;
- query.QueryData.QueryStart = queryStart;
- query.QueryData.SearchType = (uint)typeFlags;
- query.QueryData.QueryFlags = (uint)findFlags;
- query.QueryData.QueryID = queryID;
-
- Client.Network.SendPacket(query);
-
- return queryID;
- }
-
- private void DirClassifiedReplyHandler(Packet packet, Simulator simulator)
- {
- if (OnClassifiedReply != null)
- {
- DirClassifiedReplyPacket reply = (DirClassifiedReplyPacket)packet;
- List classifieds = new List();
-
- foreach (DirClassifiedReplyPacket.QueryRepliesBlock block in reply.QueryReplies)
- {
- Classified classified = new Classified();
-
- classified.CreationDate = Helpers.UnixTimeToDateTime(block.CreationDate);
- classified.ExpirationDate = Helpers.UnixTimeToDateTime(block.ExpirationDate);
- classified.Flags = block.ClassifiedFlags;
- classified.ID = block.ClassifiedID;
- classified.Name = Helpers.FieldToString(block.Name);
- classified.Price = block.PriceForListing;
-
- classifieds.Add(classified);
- }
-
- try { OnClassifiedReply(classifieds); }
- catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
- }
- }
-
- private void DirLandReplyHandler(Packet packet, Simulator simulator)
- {
- if (OnDirLandReply != null)
- {
- List parcelsForSale = new List();
- DirLandReplyPacket reply = (DirLandReplyPacket)packet;
-
- foreach (DirLandReplyPacket.QueryRepliesBlock block in reply.QueryReplies)
- {
- DirectoryParcel dirParcel = new DirectoryParcel();
-
- dirParcel.ActualArea = block.ActualArea;
- dirParcel.ID = block.ParcelID;
- dirParcel.Name = Helpers.FieldToString(block.Name);
- dirParcel.SalePrice = block.SalePrice;
- dirParcel.Auction = block.Auction;
- dirParcel.ForSale = block.ForSale;
- dirParcel.ReservedNewbie = block.ReservedNewbie;
-
- parcelsForSale.Add(dirParcel);
- }
-
- try { OnDirLandReply(parcelsForSale); }
- catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
- }
- }
- }
-}
+/*
+ * 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 System.Threading;
+using libsecondlife.Packets;
+
+namespace libsecondlife
+{
+ ///
+ ///
+ ///
+ public class DirectoryManager
+ {
+ ///
+ /// The different categories a classified ad can be placed in
+ ///
+ public enum ClassifiedCategories
+ {
+ ///
+ Any = 0,
+ ///
+ Shopping,
+ ///
+ LandRental,
+ ///
+ PropertyRental,
+ ///
+ SpecialAttraction,
+ ///
+ NewProducts,
+ ///
+ Employment,
+ ///
+ Wanted,
+ ///
+ Service,
+ ///
+ Personal
+ }
+
+ ///
+ ///
+ ///
+ [Flags]
+ public enum DirFindFlags
+ {
+ ///
+ People = 1 << 0,
+ ///
+ Online = 1 << 1,
+ ///
+ [Obsolete]
+ Places = 1 << 2,
+ ///
+ Events = 1 << 3,
+ ///
+ Groups = 1 << 4,
+ ///
+ DateEvents = 1 << 5,
+ ///
+ AgentOwned = 1 << 6,
+ ///
+ ForSale = 1 << 7,
+ ///
+ GroupOwned = 1 << 8,
+ ///
+ [Obsolete]
+ Auction = 1 << 9,
+ ///
+ DwellSort = 1 << 10,
+ ///
+ PgSimsOnly = 1 << 11,
+ ///
+ PicturesOnly = 1 << 12,
+ ///
+ PgEventsOnly = 1 << 13,
+ ///
+ MatureSimsOnly = 1 << 14,
+ ///
+ SortAsc = 1 << 15,
+ ///
+ PricesSort = 1 << 16,
+ ///
+ PerMeterSort = 1 << 17,
+ ///
+ AreaSort = 1 << 18,
+ ///
+ NameSort = 1 << 19,
+ ///
+ LimitByPrice = 1 << 20,
+ ///
+ LimitByArea = 1 << 21
+ }
+
+ ///
+ ///
+ ///
+ [Flags]
+ public enum SearchTypeFlags
+ {
+ ///
+ None = 0,
+ ///
+ Auction = 1 << 1,
+ ///
+ Newbie = 1 << 2,
+ ///
+ Mainland = 1 << 3,
+ ///
+ Estate = 1 << 4
+ }
+
+
+ ///
+ /// A classified ad in Second Life
+ ///
+ public struct Classified
+ {
+ /// UUID for this ad, useful for looking up detailed
+ /// information about it
+ public LLUUID ID;
+ /// The title of this classified ad
+ public string Name;
+ /// Unknown
+ public byte Flags;
+ /// Creation date of the ad
+ public DateTime CreationDate;
+ /// Expiration date of the ad
+ public DateTime ExpirationDate;
+ /// Price that was paid for this ad
+ public int Price;
+ }
+
+ ///
+ /// A parcel retrieved from the dataserver such as results from the
+ /// "For-Sale" listings
+ ///
+ public struct DirectoryParcel
+ {
+ ///
+ public LLUUID ID;
+ ///
+ public string Name;
+ ///
+ public int ActualArea;
+ ///
+ public int SalePrice;
+ ///
+ public bool Auction;
+ ///
+ public bool ForSale;
+ ///
+ public bool ReservedNewbie;
+ }
+
+ /*///
+ public LLUUID OwnerID;
+ ///
+ public LLUUID SnapshotID;
+ ///
+ public ulong RegionHandle;
+ ///
+ public string SimName;
+ ///
+ public string Desc;
+ ///
+ public LLVector3 GlobalPosition;
+ ///
+ public LLVector3 SimPosition;
+ ///
+ public float Dwell;*/
+
+
+ ///
+ ///
+ ///
+ ///
+ public delegate void ClassifiedReplyCallback(List classifieds);
+ ///
+ ///
+ ///
+ ///
+ public delegate void DirLandReplyCallback(List dirParcels);
+
+
+ ///
+ ///
+ ///
+ public event ClassifiedReplyCallback OnClassifiedReply;
+ ///
+ ///
+ ///
+ public event DirLandReplyCallback OnDirLandReply;
+
+
+ private SecondLife Client;
+
+
+ public DirectoryManager(SecondLife client)
+ {
+ Client = client;
+
+ Client.Network.RegisterCallback(PacketType.DirClassifiedReply, new NetworkManager.PacketCallback(DirClassifiedReplyHandler));
+ Client.Network.RegisterCallback(PacketType.DirLandReply, new NetworkManager.PacketCallback(DirLandReplyHandler));
+ }
+
+ public LLUUID StartClassifiedSearch(string searchText, ClassifiedCategories categories, bool mature)
+ {
+ DirClassifiedQueryPacket query = new DirClassifiedQueryPacket();
+ LLUUID queryID = LLUUID.Random();
+
+ query.AgentData.AgentID = Client.Network.AgentID;
+ query.AgentData.SessionID = Client.Network.SessionID;
+ query.QueryData.Category = (uint)categories;
+ query.QueryData.QueryFlags = (uint)(mature ? 0 : 2);
+ query.QueryData.QueryID = queryID;
+ query.QueryData.QueryText = Helpers.StringToField(searchText);
+
+ Client.Network.SendPacket(query);
+
+ return queryID;
+ }
+
+ ///
+ /// Starts a search for land sales using the directory
+ ///
+ /// What type of land to search for. Auction,
+ /// estate, mainland, "first land", etc
+ /// A unique identifier that can identify packets associated
+ /// with this query from other queries
+ /// The OnDirLandReply event handler must be registered before
+ /// calling this function. There is no way to determine how many
+ /// results will be returned, or how many times the callback will be
+ /// fired other than you won't get more than 100 total parcels from
+ /// each query.
+ public LLUUID StartLandSearch(SearchTypeFlags typeFlags)
+ {
+ return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort, typeFlags, 0, 0, 0);
+ }
+
+ ///
+ /// Starts a search for land sales using the directory
+ ///
+ /// What type of land to search for. Auction,
+ /// estate, mainland, "first land", etc
+ /// Maximum price to search for
+ /// Maximum area to search for
+ /// Each request is limited to 100 parcels
+ /// being returned. To get the first 100 parcels of a request use 0,
+ /// from 100-199 use 1, 200-299 use 2, etc.
+ /// A unique identifier that can identify packets associated
+ /// with this query from other queries
+ /// The OnDirLandReply event handler must be registered before
+ /// calling this function. There is no way to determine how many
+ /// results will be returned, or how many times the callback will be
+ /// fired other than you won't get more than 100 total parcels from
+ /// each query.
+ public LLUUID StartLandSearch(SearchTypeFlags typeFlags, int priceLimit, int areaLimit, int queryStart)
+ {
+ return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort | DirFindFlags.LimitByPrice |
+ DirFindFlags.LimitByArea, typeFlags, priceLimit, areaLimit, queryStart);
+ }
+
+ ///
+ /// Starts a search for land sales using the directory
+ ///
+ /// A flags parameter that can modify the way
+ /// search results are returned, for example changing the ordering of
+ /// results or limiting based on price or area
+ /// What type of land to search for. Auction,
+ /// estate, mainland, "first land", etc
+ /// Maximum price to search for, the
+ /// DirFindFlags.LimitByPrice flag must be set
+ /// Maximum area to search for, the
+ /// DirFindFlags.LimitByArea flag must be set
+ /// Each request is limited to 100 parcels
+ /// being returned. To get the first 100 parcels of a request use 0,
+ /// from 100-199 use 1, 200-299 use 2, etc.
+ /// A unique identifier that can identify packets associated
+ /// with this query from other queries
+ /// The OnDirLandReply event handler must be registered before
+ /// calling this function. There is no way to determine how many
+ /// results will be returned, or how many times the callback will be
+ /// fired other than you won't get more than 100 total parcels from
+ /// each query.
+ public LLUUID StartLandSearch(DirFindFlags findFlags, SearchTypeFlags typeFlags, int priceLimit,
+ int areaLimit, int queryStart)
+ {
+ LLUUID queryID = LLUUID.Random();
+
+ DirLandQueryPacket query = new DirLandQueryPacket();
+ query.AgentData.AgentID = Client.Network.AgentID;
+ query.AgentData.SessionID = Client.Network.SessionID;
+ query.QueryData.Area = areaLimit;
+ query.QueryData.Price = priceLimit;
+ query.QueryData.QueryStart = queryStart;
+ query.QueryData.SearchType = (uint)typeFlags;
+ query.QueryData.QueryFlags = (uint)findFlags;
+ query.QueryData.QueryID = queryID;
+
+ Client.Network.SendPacket(query);
+
+ return queryID;
+ }
+
+ private void DirClassifiedReplyHandler(Packet packet, Simulator simulator)
+ {
+ if (OnClassifiedReply != null)
+ {
+ DirClassifiedReplyPacket reply = (DirClassifiedReplyPacket)packet;
+ List classifieds = new List();
+
+ foreach (DirClassifiedReplyPacket.QueryRepliesBlock block in reply.QueryReplies)
+ {
+ Classified classified = new Classified();
+
+ classified.CreationDate = Helpers.UnixTimeToDateTime(block.CreationDate);
+ classified.ExpirationDate = Helpers.UnixTimeToDateTime(block.ExpirationDate);
+ classified.Flags = block.ClassifiedFlags;
+ classified.ID = block.ClassifiedID;
+ classified.Name = Helpers.FieldToString(block.Name);
+ classified.Price = block.PriceForListing;
+
+ classifieds.Add(classified);
+ }
+
+ try { OnClassifiedReply(classifieds); }
+ catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+
+ private void DirLandReplyHandler(Packet packet, Simulator simulator)
+ {
+ if (OnDirLandReply != null)
+ {
+ List parcelsForSale = new List();
+ DirLandReplyPacket reply = (DirLandReplyPacket)packet;
+
+ foreach (DirLandReplyPacket.QueryRepliesBlock block in reply.QueryReplies)
+ {
+ DirectoryParcel dirParcel = new DirectoryParcel();
+
+ dirParcel.ActualArea = block.ActualArea;
+ dirParcel.ID = block.ParcelID;
+ dirParcel.Name = Helpers.FieldToString(block.Name);
+ dirParcel.SalePrice = block.SalePrice;
+ dirParcel.Auction = block.Auction;
+ dirParcel.ForSale = block.ForSale;
+ dirParcel.ReservedNewbie = block.ReservedNewbie;
+
+ parcelsForSale.Add(dirParcel);
+ }
+
+ try { OnDirLandReply(parcelsForSale); }
+ catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+ }
+}
diff --git a/libsecondlife-cs/Helpers.cs b/libsecondlife-cs/Helpers.cs
index dd970be6..ecfc6e2f 100644
--- a/libsecondlife-cs/Helpers.cs
+++ b/libsecondlife-cs/Helpers.cs
@@ -1,945 +1,945 @@
-/*
- * 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 System.Xml;
-using System.Xml.Serialization;
-using System.Text;
-using libsecondlife.Packets;
-
-namespace libsecondlife
-{
- ///
- /// Static helper functions and global variables
- ///
- public class Helpers
- {
- /// This header flag signals that ACKs are appended to the packet
- public const byte MSG_APPENDED_ACKS = 0x10;
- /// This header flag signals that this packet has been sent before
- public const byte MSG_RESENT = 0x20;
- /// This header flags signals that an ACK is expected for this packet
- public const byte MSG_RELIABLE = 0x40;
- /// This header flag signals that the message is compressed using zerocoding
- public const byte MSG_ZEROCODED = 0x80;
- /// Used for converting degrees to radians
- public const double DEG_TO_RAD = Math.PI / 180.0d;
- /// Used for converting radians to degrees
- public const double RAD_TO_DEG = 180.0d / Math.PI;
-
- ///
- /// Passed to SecondLife.Log() to identify the severity of a log entry
- ///
- public enum LogLevel
- {
- /// Non-noisy useful information, may be helpful in
- /// debugging a problem
- Info,
- /// A non-critical error occurred. A warning will not
- /// prevent the rest of libsecondlife from operating as usual,
- /// although it may be indicative of an underlying issue
- Warning,
- /// A critical error has occurred. Generally this will
- /// be followed by the network layer shutting down, although the
- /// stability of libsecondlife after an error is uncertain
- Error,
- /// Used for internal testing, this logging level can
- /// generate very noisy (long and/or repetitive) messages. Don't
- /// pass this to the Log() function, use DebugLog() instead.
- ///
- Debug
- };
-
- ///
- ///
- ///
- [Flags]
- public enum PermissionWho
- {
- ///
- Group = 4,
- ///
- Everyone = 8,
- ///
- NextOwner = 16
- }
-
- ///
- ///
- ///
- [Flags]
- public enum PermissionType
- {
- ///
- Copy = 0x00008000,
- ///
- Modify = 0x00004000,
- ///
- Move = 0x00080000,
- ///
- Transfer = 0x00002000
- }
-
- /// Provide a single instance of the MD5 class to avoid making
- /// duplicate copies
- public static System.Security.Cryptography.MD5 MD5Builder =
- new System.Security.Cryptography.MD5CryptoServiceProvider();
-
- ///
- /// Converts an unsigned integer to a hexadecimal string
- ///
- /// An unsigned integer to convert to a string
- /// A hexadecimal string 10 characters long
- /// 0x7fffffff
- public static string UIntToHexString(uint i)
- {
- return string.Format("{0:x8}", i);
- }
-
- ///
- /// Packs to 32-bit unsigned integers in to a 64-bit unsigned integer
- ///
- /// The left-hand (or X) value
- /// The right-hand (or Y) value
- /// A 64-bit integer containing the two 32-bit input values
- public static ulong UIntsToLong(uint a, uint b)
- {
- return (ulong)(((ulong)a << 32) + (ulong)b);
- }
-
- ///
- /// Unpacks two 32-bit unsigned integers from a 64-bit unsigned integer
- ///
- /// The 64-bit input integer
- /// The left-hand (or X) output value
- /// The right-hand (or Y) output value
- public static void LongToUInts(ulong a, out uint b, out uint c)
- {
- b = (uint)(a >> 32);
- c = (uint)(a & 0x00000000FFFFFFFF);
- }
-
- ///
- /// Convert an integer to a byte array in little endian format
- ///
- /// The integer to convert
- /// A four byte little endian array
- public static byte[] IntToBytes(int x)
- {
- byte[] bytes = new byte[4];
-
- bytes[0]= (byte)(x % 256);
- bytes[1] = (byte)((x >> 8) % 256);
- bytes[2] = (byte)((x >> 16) % 256);
- bytes[3] = (byte)((x >> 24) % 256);
-
- return bytes;
- }
-
- ///
- /// Convert the first two bytes starting at the given position in
- /// little endian ordering to an unsigned short
- ///
- /// Byte array containing the ushort
- /// Position to start reading the ushort from
- /// An unsigned short, will be zero if a ushort can't be read
- /// at the given position
- public static ushort BytesToUInt16(byte[] bytes, int pos)
- {
- if (bytes.Length <= pos + 1) return 0;
- return (ushort)(bytes[pos] + (bytes[pos + 1] << 8));
- }
-
- ///
- /// Convert the first four bytes starting at the given position in
- /// little endian ordering to an unsigned integer
- ///
- /// Byte array containing the uint
- /// Position to start reading the uint from
- /// An unsigned integer, will be zero if a uint can't be read
- /// at the given position
- public static uint BytesToUInt(byte[] bytes, int pos)
- {
- if (bytes.Length <= pos + 4) return 0;
- return (uint)(bytes[pos + 3] + (bytes[pos + 2] << 8) + (bytes[pos + 1] << 16) + (bytes[pos] << 24));
- }
-
- ///
- /// Convert the first four bytes of the given array in little endian
- /// ordering to an unsigned integer
- ///
- /// An array four bytes or longer
- /// An unsigned integer, will be zero if the array contains
- /// less than four bytes
- public static uint BytesToUInt(byte[] bytes)
- {
- if (bytes.Length < 4) return 0;
- return (uint)(bytes[3] + (bytes[2] << 8) + (bytes[1] << 16) + (bytes[0] << 24));
- }
-
- ///
- /// Convert the first four bytes starting at the given position in
- /// big endian ordering to an unsigned integer
- ///
- /// Byte array containing the uint
- /// Position to start reading the uint from
- /// An unsigned integer, will be zero if a uint can't be read
- /// at the given position
- public static uint BytesToUIntBig(byte[] bytes, int pos)
- {
- if (bytes.Length <= pos + 4) return 0;
- return (uint)(bytes[pos] + (bytes[pos + 1] << 8) + (bytes[pos + 2] << 16) + (bytes[pos + 3] << 24));
- }
-
- ///
- /// Convert the first four bytes of the given array in big endian
- /// ordering to an unsigned integer
- ///
- /// An array four bytes or longer
- /// An unsigned integer, will be zero if the array contains
- /// less than four bytes
- public static uint BytesToUIntBig(byte[] bytes)
- {
- if (bytes.Length < 4) return 0;
- return (uint)(bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24));
- }
-
- ///
- /// Convert the first eight bytes of the given array in little endian
- /// ordering to an unsigned 64-bit integer
- ///
- /// An array eight bytes or longer
- /// An unsigned 64-bit integer, will be zero if the array
- /// contains less than eight bytes
- public static ulong BytesToUInt64(byte[] bytes)
- {
- if (bytes.Length < 8) return 0;
- return (ulong)
- ((ulong)bytes[7] +
- ((ulong)bytes[6] << 8) +
- ((ulong)bytes[5] << 16) +
- ((ulong)bytes[4] << 24) +
- ((ulong)bytes[3] << 32) +
- ((ulong)bytes[2] << 40) +
- ((ulong)bytes[1] << 48) +
- ((ulong)bytes[0] << 56));
- }
-
- ///
- /// Converts a floating point number to a terse string format used for
- /// transmitting numbers in wearable asset files
- ///
- /// Floating point number to convert to a string
- /// A terse string representation of the input number
- public static string FloatToTerseString(float val)
- {
- string s = string.Format("{0:.00}", val);
-
- // Trim trailing zeroes
- while (s[s.Length - 1] == '0')
- s = s.Remove(s.Length - 1);
-
- // Remove superfluous decimal places after the trim
- if (s[s.Length - 1] == '.')
- s = s.Remove(s.Length - 1);
- // Remove leading zeroes after a negative sign
- else if (s[0] == '-' && s[1] == '0')
- s = s.Remove(1, 1);
- // Remove leading zeroes in positive numbers
- else if (s[0] == '0')
- s = s.Remove(0, 1);
-
- return s;
- }
-
- ///
- /// Convert a float value to a byte given a minimum and maximum range
- ///
- /// Value to convert to a byte
- /// Minimum value range
- /// Maximum value range
- /// A single byte representing the original float value
- public static byte FloatToByte(float val, float lower, float upper)
- {
- val = Clamp(val, lower, upper);
- // Normalize the value
- val -= lower;
- val /= (upper - lower);
-
- return (byte)Math.Floor(val * (float)byte.MaxValue);
- }
-
- ///
- /// Convert a byte to a float value given a minimum and maximum range
- ///
- /// Byte array to get the byte from
- /// Position in the byte array the desired byte is at
- /// Minimum value range
- /// Maximum value range
- /// A float value inclusively between lower and upper
- public static float ByteToFloat(byte[] bytes, int pos, float lower, float upper)
- {
- if (bytes.Length <= pos) return 0;
- return ByteToFloat(bytes[pos], lower, upper);
- }
-
- ///
- /// Convert a byte to a float value given a minimum and maximum range
- ///
- /// Byte to convert to a float value
- /// Minimum value range
- /// Maximum value range
- /// A float value inclusively between lower and upper
- public static float ByteToFloat(byte val, float lower, float upper)
- {
- const float ONE_OVER_BYTEMAX = 1.0f / (float)byte.MaxValue;
-
- float fval = (float)val * ONE_OVER_BYTEMAX;
- float delta = (upper - lower);
- fval *= delta;
- fval += lower;
-
- // Test for values very close to zero
- float error = delta * ONE_OVER_BYTEMAX;
- if (Math.Abs(fval) < error)
- fval = 0.0f;
-
- return fval;
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static float UInt16ToFloat(byte[] bytes, int pos, float lower, float upper)
- {
- ushort val = BytesToUInt16(bytes, pos);
- return UInt16ToFloat(val, lower, upper);
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static float UInt16ToFloat(ushort val, float lower, float upper)
- {
- const float ONE_OVER_U16_MAX = 1.0f / 65535.0f;
-
- float fval = (float)val * ONE_OVER_U16_MAX;
- float delta = upper - lower;
- fval *= delta;
- fval += lower;
-
- // Make sure zeroes come through as zero
- float maxError = delta * ONE_OVER_U16_MAX;
- if (Math.Abs(fval) < maxError)
- fval = 0.0f;
-
- return fval;
- }
-
- ///
- /// Clamp a given value between a range
- ///
- /// Value to clamp
- /// Minimum allowable value
- /// Maximum allowable value
- /// A value inclusively between lower and upper
- public static float Clamp(float val, float lower, float upper)
- {
- return Math.Min(Math.Max(val, lower), upper);
- }
-
- ///
- /// Convert a variable length field (byte array) to a UTF8 string
- ///
- /// The byte array to convert to a string
- /// A UTF8 string
- public static string FieldToUTF8String(byte[] bytes)
- {
- if (bytes.Length > 0 && bytes[bytes.Length - 1] == 0x00)
- return UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1);
- else
- return UTF8Encoding.UTF8.GetString(bytes);
- }
-
- ///
- /// Convert a variable length field (byte array) to a string
- ///
- /// If the byte array has unprintable characters in it, a
- /// hex dump will be put in the string instead
- /// The byte array to convert to a string
- /// An ASCII string or a string containing a hex dump, minus
- /// the null terminator
- public static string FieldToString(byte[] bytes)
- {
- return FieldToString(bytes, String.Empty);
- }
-
- ///
- /// Convert a variable length field (byte array) to a string, with a
- /// field name prepended to each line of the output
- ///
- /// If the byte array has unprintable characters in it, a
- /// hex dump will be put in the string instead
- /// The byte array to convert to a string
- /// A field name to prepend to each line of output
- /// An ASCII string or a string containing a hex dump, minus
- /// the null terminator
- public static string FieldToString(byte[] bytes, string fieldName)
- {
- // Check for a common case
- if (bytes.Length == 0) return String.Empty;
-
- StringBuilder output = new StringBuilder();
- bool printable = true;
-
- for (int i = 0; i < bytes.Length; ++i)
- {
- // Check if there are any unprintable characters in the array
- if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
- && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
- {
- printable = false;
- break;
- }
- }
-
- if (printable)
- {
- if (fieldName.Length > 0)
- {
- output.Append(fieldName);
- output.Append(": ");
- }
-
- if (bytes[bytes.Length - 1] == 0x00)
- output.Append(UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1));
- else
- output.Append(UTF8Encoding.UTF8.GetString(bytes));
- }
- else
- {
- for (int i = 0; i < bytes.Length; i += 16)
- {
- if (i != 0)
- output.Append(Environment.NewLine);
- if (fieldName.Length > 0)
- {
- output.Append(fieldName);
- output.Append(": ");
- }
-
- for (int j = 0; j < 16; j++)
- {
- if ((i + j) < bytes.Length)
- output.Append(String.Format("{0:X2} ", bytes[i + j]));
- else
- output.Append(" ");
- }
-
- for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
- {
- if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
- output.Append((char)bytes[i + j]);
- else
- output.Append(".");
- }
- }
- }
-
- return output.ToString();
- }
-
- ///
- /// Converts a byte array to a string containing hexadecimal characters
- ///
- /// The byte array to convert to a string
- /// The name of the field to prepend to each
- /// line of the string
- /// A string containing hexadecimal characters on multiple
- /// lines. Each line is prepended with the field name
- public static string FieldToHexString(byte[] bytes, string fieldName)
- {
- StringBuilder output = new StringBuilder();
-
- for (int i = 0; i < bytes.Length; i += 16)
- {
- if (i != 0)
- output.Append(Environment.NewLine);
- if (fieldName.Length > 0)
- output.Append(": ");
-
- for (int j = 0; j < 16; j++)
- {
- if ((i + j) < bytes.Length)
- output.Append(String.Format("{0:X2} ", bytes[i + j]));
- else
- output.Append(" ");
- }
-
- for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
- {
- if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
- output.Append(bytes[i + j]);
- else
- output.Append(".");
- }
- }
-
- return output.ToString();
- }
-
- /////
- ///// Converts a string containing hexadecimal characters to a byte array
- /////
- ///// String containing hexadecimal characters
- ///// The converted byte array
- //public static byte[] HexStringToField(string hexString)
- //{
- // string newString = "";
- // char c;
-
- // // FIXME: For each line of the string, if a colon is found
- // // remove everything before it
-
- // // remove all non A-F, 0-9, characters
- // for (int i = 0; i < hexString.Length; i++)
- // {
- // c = hexString[i];
- // if (IsHexDigit(c))
- // newString += c;
- // }
-
- // // if odd number of characters, discard last character
- // if (newString.Length % 2 != 0)
- // {
- // newString = newString.Substring(0, newString.Length - 1);
- // }
-
- // int byteLength = newString.Length / 2;
- // byte[] bytes = new byte[byteLength];
- // string hex;
- // int j = 0;
- // for (int i = 0; i < bytes.Length; i++)
- // {
- // hex = new String(new Char[] { newString[j], newString[j + 1] });
- // bytes[i] = HexToByte(hex);
- // j = j + 2;
- // }
- // return bytes;
- //}
-
- ///
- /// Convert a UTF8 string to a byte array
- ///
- /// The string to convert to a byte array
- /// A null-terminated byte array
- public static byte[] StringToField(string str)
- {
- if (str.Length == 0) { return new byte[0]; }
- if (!str.EndsWith("\0")) { str += "\0"; }
- return System.Text.UTF8Encoding.UTF8.GetBytes(str);
- }
-
- ///
- /// Gets a unix timestamp for the current time
- ///
- /// An unsigned integer representing a unix timestamp for now
- public static uint GetUnixTime()
- {
- return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
- }
-
- ///
- /// Convert a unix timestamp to a native DateTime format
- ///
- /// An unsigned integer representing a unix
- /// timestamp
- /// A DateTime object containing the same time specified in
- /// the given timestamp
- public static DateTime UnixTimeToDateTime(uint timestamp)
- {
- // Make a DateTime equivalent to the UNIX Epoch
- System.DateTime dateTime = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
-
- // Add the number of seconds in our UNIX timestamp
- dateTime = dateTime.AddSeconds(timestamp);
-
- return dateTime;
- }
-
- ///
- /// Converts a vector style rotation to a quaternion
- ///
- /// Axis rotation, such as 0,0,90 for 90 degrees to the right
- /// A quaternion representing the axes of the supplied vector
- public static LLQuaternion Axis2Rot(LLVector3 a)
- {
- if (a.X > 180) a.X -= 360; if (a.Y > 180) a.Y -= 360; if (a.Z > 180) a.Z -= 360;
- if (a.X < -180) a.X += 360; if (a.Y < -180) a.Y += 360; if (a.Z < -180) a.Z += 360;
-
- LLQuaternion rot = LLQuaternion.Identity;
- rot.X = (float)(a.X * DEG_TO_RAD);
- rot.Y = (float)(a.Y * DEG_TO_RAD);
- rot.Z = (float)(a.Z * DEG_TO_RAD);
- if (a.Z > 180) rot.W = 0;
-
- return rot;
- }
-
- ///
- /// Calculates the distance between two vectors
- ///
- public static float VecDist(LLVector3 pointA, LLVector3 pointB)
- {
- float xd = pointB.X - pointA.X;
- float yd = pointB.Y - pointA.Y;
- float zd = pointB.Z - pointA.Z;
- return (float)Math.Sqrt(xd * xd + yd * yd + zd * zd);
- }
-
- ///
- /// Calculate the magnitude of the supplied vector
- ///
- public static float VecMag(LLVector3 v)
- {
- return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
- }
-
- ///
- /// Calculate the magnitude of the supplied quaternion
- ///
- public static float RotMag(LLQuaternion q)
- {
- return (float)Math.Sqrt(q.W * q.W + q.X * q.X + q.Y * q.Y + q.Z * q.Z);
- }
-
- ///
- /// Return the supplied vector in normalized form
- ///
- public static LLVector3 VecNorm(LLVector3 vector)
- {
- float mag = VecMag(vector);
- return new LLVector3(vector.X / mag, vector.Y / mag, vector.Z / mag);
- }
-
- ///
- /// Calculate the rotation between two vectors
- ///
- /// Directional vector, such as 1,0,0 for the forward face
- /// Target vector - normalize first with VecNorm
- public static LLQuaternion RotBetween(LLVector3 a, LLVector3 b)
- {
- //A and B should both be normalized
- //dotProduct is 0 if a and b are perpendicular. I think that's normal?
- float dotProduct = (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z);
-
- LLVector3 crossProduct = new LLVector3();
- crossProduct.X = a.Y * b.Z - a.Z * b.Y;
- crossProduct.Y = a.Z * b.X - a.X * b.Z;
- crossProduct.Z = a.X * b.Y - a.Y * b.X;
-
- //float scalarProduct = (a.X * b.Y) + (a.Y * b.Z) + (a.Z * b.X); //not used?
- float magProduct = VecMag(a) * VecMag(b);
- double angle = Math.Acos(dotProduct / magProduct);
-
- LLVector3 axis = VecNorm(crossProduct);
- float s = (float)Math.Sin(angle / 2);
- return new LLQuaternion(axis.X * s, axis.Y * s, axis.Z * s, (float)Math.Cos(angle / 2));
- }
-
- ///
- /// Decode a zerocoded byte array, used to decompress packets marked
- /// with the zerocoded flag
- ///
- /// Any time a zero is encountered, the next byte is a count
- /// of how many zeroes to expand. One zero is encoded with 0x00 0x01,
- /// two zeroes is 0x00 0x02, three zeroes is 0x00 0x03, etc. The
- /// first four bytes are copied directly to the output buffer.
- ///
- /// The byte array to decode
- /// The length of the byte array to decode. This
- /// would be the length of the packet up to (but not including) any
- /// appended ACKs
- /// The output byte array to decode to
- /// The length of the output buffer
- public static int ZeroDecode(byte[] src, int srclen, byte[] dest)
- {
- uint zerolen = 0;
- int bodylen = 0;
- uint i = 0;
-
- try
- {
- Array.Copy(src, 0, dest, 0, 4);
- zerolen = 4;
- bodylen = srclen;
-
- for (i = zerolen; i < bodylen; i++)
- {
- if (src[i] == 0x00)
- {
- for (byte j = 0; j < src[i + 1]; j++)
- {
- dest[zerolen++] = 0x00;
- }
-
- i++;
- }
- else
- {
- dest[zerolen++] = src[i];
- }
- }
-
- // Copy appended ACKs
- for (; i < srclen; i++)
- {
- dest[zerolen++] = src[i];
- }
-
- return (int)zerolen;
- }
- catch (Exception e)
- {
- Console.WriteLine("Zerodecoding error: " + Environment.NewLine +
- "i=" + i + "srclen=" + srclen + ", bodylen=" + bodylen + ", zerolen=" + zerolen + Environment.NewLine +
- FieldToString(src, "src") + Environment.NewLine +
- e.ToString());
- }
-
- return 0;
- }
-
- ///
- /// Decode enough of a byte array to get the packet ID. Data before and
- /// after the packet ID is undefined.
- ///
- /// The byte array to decode
- /// The output byte array to encode to
- public static void ZeroDecodeCommand(byte[] src, byte[] dest)
- {
- for (int srcPos = 4, destPos = 4; destPos < 8; ++srcPos)
- {
- if (src[srcPos] == 0x00)
- {
- for (byte j = 0; j < src[srcPos + 1]; ++j)
- {
- dest[destPos++] = 0x00;
- }
-
- ++srcPos;
- }
- else
- {
- dest[destPos++] = src[srcPos];
- }
- }
- }
-
- ///
- /// Encode a byte array with zerocoding. Used to compress packets marked
- /// with the zerocoded flag. Any zeroes in the array are compressed down
- /// to a single zero byte followed by a count of how many zeroes to expand
- /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02,
- /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied
- /// directly to the output buffer.
- ///
- /// The byte array to encode
- /// The length of the byte array to encode
- /// The output byte array to encode to
- /// The length of the output buffer
- public static int ZeroEncode(byte[] src, int srclen, byte[] dest)
- {
- uint zerolen = 0;
- byte zerocount = 0;
-
- Array.Copy(src, 0, dest, 0, 4);
- zerolen += 4;
-
- int bodylen;
- if ((src[0] & MSG_APPENDED_ACKS) == 0)
- {
- bodylen = srclen;
- }
- else
- {
- bodylen = srclen - src[srclen - 1] * 4 - 1;
- }
-
- uint i;
- for (i = zerolen; i < bodylen; i++)
- {
- if (src[i] == 0x00)
- {
- zerocount++;
-
- if (zerocount == 0)
- {
- dest[zerolen++] = 0x00;
- dest[zerolen++] = 0xff;
- zerocount++;
- }
- }
- else
- {
- if (zerocount != 0)
- {
- dest[zerolen++] = 0x00;
- dest[zerolen++] = (byte)zerocount;
- zerocount = 0;
- }
-
- dest[zerolen++] = src[i];
- }
- }
-
- if (zerocount != 0)
- {
- dest[zerolen++] = 0x00;
- dest[zerolen++] = (byte)zerocount;
- }
-
- // copy appended ACKs
- for (; i < srclen; i++)
- {
- dest[zerolen++] = src[i];
- }
-
- return (int)zerolen;
- }
-
- ///
- /// Calculates the CRC (cyclic redundancy check) needed to upload inventory.
- ///
- /// Creation date
- /// Sale type
- /// Inventory type
- /// Type
- /// Asset ID
- /// Group ID
- /// Sale price
- /// Owner ID
- /// Creator ID
- /// Item ID
- /// Folder ID
- /// Everyone mask (permissions)
- /// Flags
- /// Next owner mask (permissions)
- /// Group mask (permissions)
- /// Owner mask (permisions)
- /// The calculated CRC
- public static uint InventoryCRC(int creationDate, byte saleType, sbyte invType, sbyte type,
- LLUUID assetID, LLUUID groupID, int salePrice, LLUUID ownerID, LLUUID creatorID,
- LLUUID itemID, LLUUID folderID, uint everyoneMask, uint flags, uint nextOwnerMask,
- uint groupMask, uint ownerMask)
- {
- uint CRC = 0;
-
- // IDs
- CRC += assetID.CRC(); // AssetID
- CRC += folderID.CRC(); // FolderID
- CRC += itemID.CRC(); // ItemID
-
- // Permission stuff
- CRC += creatorID.CRC(); // CreatorID
- CRC += ownerID.CRC(); // OwnerID
- CRC += groupID.CRC(); // GroupID
-
- // CRC += another 4 words which always seem to be zero -- unclear if this is a LLUUID or what
- CRC += ownerMask;
- CRC += nextOwnerMask;
- CRC += everyoneMask;
- CRC += groupMask;
-
- // The rest of the CRC fields
- CRC += flags; // Flags
- CRC += (uint)invType; // InvType
- CRC += (uint)type; // Type
- CRC += (uint)creationDate; // CreationDate
- CRC += (uint)salePrice; // SalePrice
- CRC += (uint)((uint)saleType * 0x07073096); // SaleType
-
- return CRC;
- }
-
- ///
- /// Calculate the MD5 hash of a given string
- ///
- /// The password to hash
- /// An MD5 hash in string format, with $1$ prepended
- public static string MD5(string password)
- {
- StringBuilder digest = new StringBuilder();
- byte[] hash = MD5Builder.ComputeHash(ASCIIEncoding.Default.GetBytes(password));
-
- // Convert the hash to a hex string
- foreach (byte b in hash)
- {
- digest.AppendFormat("{0:x2}", b);
- }
-
- return "$1$" + digest.ToString();
- }
-
- public static void PacketListToXml(List packets, XmlWriter xmlWriter)
- {
- //XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
- //ns.Add("", "");
- XmlSerializer serializer = new XmlSerializer(typeof(List));
- serializer.Serialize(xmlWriter, packets);
- }
-
- public static void PrimListToXml(List list, XmlWriter xmlWriter)
- {
- XmlSerializer serializer = new XmlSerializer(typeof(List));
- serializer.Serialize(xmlWriter, list);
- }
-
- public static List PrimListFromXml(XmlReader reader)
- {
- XmlSerializer serializer = new XmlSerializer(typeof(List));
- object list = serializer.Deserialize(reader);
- return (List)list;
- }
-
- public static List PacketListFromXml(XmlReader reader)
- {
- XmlSerializer serializer = new XmlSerializer(typeof(List));
- object list = serializer.Deserialize(reader);
- return (List)list;
- }
- }
-}
+/*
+ * 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 System.Xml;
+using System.Xml.Serialization;
+using System.Text;
+using libsecondlife.Packets;
+
+namespace libsecondlife
+{
+ ///
+ /// Static helper functions and global variables
+ ///
+ public class Helpers
+ {
+ /// This header flag signals that ACKs are appended to the packet
+ public const byte MSG_APPENDED_ACKS = 0x10;
+ /// This header flag signals that this packet has been sent before
+ public const byte MSG_RESENT = 0x20;
+ /// This header flags signals that an ACK is expected for this packet
+ public const byte MSG_RELIABLE = 0x40;
+ /// This header flag signals that the message is compressed using zerocoding
+ public const byte MSG_ZEROCODED = 0x80;
+ /// Used for converting degrees to radians
+ public const double DEG_TO_RAD = Math.PI / 180.0d;
+ /// Used for converting radians to degrees
+ public const double RAD_TO_DEG = 180.0d / Math.PI;
+
+ ///
+ /// Passed to SecondLife.Log() to identify the severity of a log entry
+ ///
+ public enum LogLevel
+ {
+ /// Non-noisy useful information, may be helpful in
+ /// debugging a problem
+ Info,
+ /// A non-critical error occurred. A warning will not
+ /// prevent the rest of libsecondlife from operating as usual,
+ /// although it may be indicative of an underlying issue
+ Warning,
+ /// A critical error has occurred. Generally this will
+ /// be followed by the network layer shutting down, although the
+ /// stability of libsecondlife after an error is uncertain
+ Error,
+ /// Used for internal testing, this logging level can
+ /// generate very noisy (long and/or repetitive) messages. Don't
+ /// pass this to the Log() function, use DebugLog() instead.
+ ///
+ Debug
+ };
+
+ ///
+ ///
+ ///
+ [Flags]
+ public enum PermissionWho
+ {
+ ///
+ Group = 4,
+ ///
+ Everyone = 8,
+ ///
+ NextOwner = 16
+ }
+
+ ///
+ ///
+ ///
+ [Flags]
+ public enum PermissionType
+ {
+ ///
+ Copy = 0x00008000,
+ ///
+ Modify = 0x00004000,
+ ///
+ Move = 0x00080000,
+ ///
+ Transfer = 0x00002000
+ }
+
+ /// Provide a single instance of the MD5 class to avoid making
+ /// duplicate copies
+ public static System.Security.Cryptography.MD5 MD5Builder =
+ new System.Security.Cryptography.MD5CryptoServiceProvider();
+
+ ///
+ /// Converts an unsigned integer to a hexadecimal string
+ ///
+ /// An unsigned integer to convert to a string
+ /// A hexadecimal string 10 characters long
+ /// 0x7fffffff
+ public static string UIntToHexString(uint i)
+ {
+ return string.Format("{0:x8}", i);
+ }
+
+ ///
+ /// Packs to 32-bit unsigned integers in to a 64-bit unsigned integer
+ ///
+ /// The left-hand (or X) value
+ /// The right-hand (or Y) value
+ /// A 64-bit integer containing the two 32-bit input values
+ public static ulong UIntsToLong(uint a, uint b)
+ {
+ return (ulong)(((ulong)a << 32) + (ulong)b);
+ }
+
+ ///
+ /// Unpacks two 32-bit unsigned integers from a 64-bit unsigned integer
+ ///
+ /// The 64-bit input integer
+ /// The left-hand (or X) output value
+ /// The right-hand (or Y) output value
+ public static void LongToUInts(ulong a, out uint b, out uint c)
+ {
+ b = (uint)(a >> 32);
+ c = (uint)(a & 0x00000000FFFFFFFF);
+ }
+
+ ///
+ /// Convert an integer to a byte array in little endian format
+ ///
+ /// The integer to convert
+ /// A four byte little endian array
+ public static byte[] IntToBytes(int x)
+ {
+ byte[] bytes = new byte[4];
+
+ bytes[0]= (byte)(x % 256);
+ bytes[1] = (byte)((x >> 8) % 256);
+ bytes[2] = (byte)((x >> 16) % 256);
+ bytes[3] = (byte)((x >> 24) % 256);
+
+ return bytes;
+ }
+
+ ///
+ /// Convert the first two bytes starting at the given position in
+ /// little endian ordering to an unsigned short
+ ///
+ /// Byte array containing the ushort
+ /// Position to start reading the ushort from
+ /// An unsigned short, will be zero if a ushort can't be read
+ /// at the given position
+ public static ushort BytesToUInt16(byte[] bytes, int pos)
+ {
+ if (bytes.Length <= pos + 1) return 0;
+ return (ushort)(bytes[pos] + (bytes[pos + 1] << 8));
+ }
+
+ ///
+ /// Convert the first four bytes starting at the given position in
+ /// little endian ordering to an unsigned integer
+ ///
+ /// Byte array containing the uint
+ /// Position to start reading the uint from
+ /// An unsigned integer, will be zero if a uint can't be read
+ /// at the given position
+ public static uint BytesToUInt(byte[] bytes, int pos)
+ {
+ if (bytes.Length <= pos + 4) return 0;
+ return (uint)(bytes[pos + 3] + (bytes[pos + 2] << 8) + (bytes[pos + 1] << 16) + (bytes[pos] << 24));
+ }
+
+ ///
+ /// Convert the first four bytes of the given array in little endian
+ /// ordering to an unsigned integer
+ ///
+ /// An array four bytes or longer
+ /// An unsigned integer, will be zero if the array contains
+ /// less than four bytes
+ public static uint BytesToUInt(byte[] bytes)
+ {
+ if (bytes.Length < 4) return 0;
+ return (uint)(bytes[3] + (bytes[2] << 8) + (bytes[1] << 16) + (bytes[0] << 24));
+ }
+
+ ///
+ /// Convert the first four bytes starting at the given position in
+ /// big endian ordering to an unsigned integer
+ ///
+ /// Byte array containing the uint
+ /// Position to start reading the uint from
+ /// An unsigned integer, will be zero if a uint can't be read
+ /// at the given position
+ public static uint BytesToUIntBig(byte[] bytes, int pos)
+ {
+ if (bytes.Length <= pos + 4) return 0;
+ return (uint)(bytes[pos] + (bytes[pos + 1] << 8) + (bytes[pos + 2] << 16) + (bytes[pos + 3] << 24));
+ }
+
+ ///
+ /// Convert the first four bytes of the given array in big endian
+ /// ordering to an unsigned integer
+ ///
+ /// An array four bytes or longer
+ /// An unsigned integer, will be zero if the array contains
+ /// less than four bytes
+ public static uint BytesToUIntBig(byte[] bytes)
+ {
+ if (bytes.Length < 4) return 0;
+ return (uint)(bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24));
+ }
+
+ ///
+ /// Convert the first eight bytes of the given array in little endian
+ /// ordering to an unsigned 64-bit integer
+ ///
+ /// An array eight bytes or longer
+ /// An unsigned 64-bit integer, will be zero if the array
+ /// contains less than eight bytes
+ public static ulong BytesToUInt64(byte[] bytes)
+ {
+ if (bytes.Length < 8) return 0;
+ return (ulong)
+ ((ulong)bytes[7] +
+ ((ulong)bytes[6] << 8) +
+ ((ulong)bytes[5] << 16) +
+ ((ulong)bytes[4] << 24) +
+ ((ulong)bytes[3] << 32) +
+ ((ulong)bytes[2] << 40) +
+ ((ulong)bytes[1] << 48) +
+ ((ulong)bytes[0] << 56));
+ }
+
+ ///
+ /// Converts a floating point number to a terse string format used for
+ /// transmitting numbers in wearable asset files
+ ///
+ /// Floating point number to convert to a string
+ /// A terse string representation of the input number
+ public static string FloatToTerseString(float val)
+ {
+ string s = string.Format("{0:.00}", val);
+
+ // Trim trailing zeroes
+ while (s[s.Length - 1] == '0')
+ s = s.Remove(s.Length - 1);
+
+ // Remove superfluous decimal places after the trim
+ if (s[s.Length - 1] == '.')
+ s = s.Remove(s.Length - 1);
+ // Remove leading zeroes after a negative sign
+ else if (s[0] == '-' && s[1] == '0')
+ s = s.Remove(1, 1);
+ // Remove leading zeroes in positive numbers
+ else if (s[0] == '0')
+ s = s.Remove(0, 1);
+
+ return s;
+ }
+
+ ///
+ /// Convert a float value to a byte given a minimum and maximum range
+ ///
+ /// Value to convert to a byte
+ /// Minimum value range
+ /// Maximum value range
+ /// A single byte representing the original float value
+ public static byte FloatToByte(float val, float lower, float upper)
+ {
+ val = Clamp(val, lower, upper);
+ // Normalize the value
+ val -= lower;
+ val /= (upper - lower);
+
+ return (byte)Math.Floor(val * (float)byte.MaxValue);
+ }
+
+ ///
+ /// Convert a byte to a float value given a minimum and maximum range
+ ///
+ /// Byte array to get the byte from
+ /// Position in the byte array the desired byte is at
+ /// Minimum value range
+ /// Maximum value range
+ /// A float value inclusively between lower and upper
+ public static float ByteToFloat(byte[] bytes, int pos, float lower, float upper)
+ {
+ if (bytes.Length <= pos) return 0;
+ return ByteToFloat(bytes[pos], lower, upper);
+ }
+
+ ///
+ /// Convert a byte to a float value given a minimum and maximum range
+ ///
+ /// Byte to convert to a float value
+ /// Minimum value range
+ /// Maximum value range
+ /// A float value inclusively between lower and upper
+ public static float ByteToFloat(byte val, float lower, float upper)
+ {
+ const float ONE_OVER_BYTEMAX = 1.0f / (float)byte.MaxValue;
+
+ float fval = (float)val * ONE_OVER_BYTEMAX;
+ float delta = (upper - lower);
+ fval *= delta;
+ fval += lower;
+
+ // Test for values very close to zero
+ float error = delta * ONE_OVER_BYTEMAX;
+ if (Math.Abs(fval) < error)
+ fval = 0.0f;
+
+ return fval;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float UInt16ToFloat(byte[] bytes, int pos, float lower, float upper)
+ {
+ ushort val = BytesToUInt16(bytes, pos);
+ return UInt16ToFloat(val, lower, upper);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float UInt16ToFloat(ushort val, float lower, float upper)
+ {
+ const float ONE_OVER_U16_MAX = 1.0f / 65535.0f;
+
+ float fval = (float)val * ONE_OVER_U16_MAX;
+ float delta = upper - lower;
+ fval *= delta;
+ fval += lower;
+
+ // Make sure zeroes come through as zero
+ float maxError = delta * ONE_OVER_U16_MAX;
+ if (Math.Abs(fval) < maxError)
+ fval = 0.0f;
+
+ return fval;
+ }
+
+ ///
+ /// Clamp a given value between a range
+ ///
+ /// Value to clamp
+ /// Minimum allowable value
+ /// Maximum allowable value
+ /// A value inclusively between lower and upper
+ public static float Clamp(float val, float lower, float upper)
+ {
+ return Math.Min(Math.Max(val, lower), upper);
+ }
+
+ ///
+ /// Convert a variable length field (byte array) to a UTF8 string
+ ///
+ /// The byte array to convert to a string
+ /// A UTF8 string
+ public static string FieldToUTF8String(byte[] bytes)
+ {
+ if (bytes.Length > 0 && bytes[bytes.Length - 1] == 0x00)
+ return UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1);
+ else
+ return UTF8Encoding.UTF8.GetString(bytes);
+ }
+
+ ///
+ /// Convert a variable length field (byte array) to a string
+ ///
+ /// If the byte array has unprintable characters in it, a
+ /// hex dump will be put in the string instead
+ /// The byte array to convert to a string
+ /// An ASCII string or a string containing a hex dump, minus
+ /// the null terminator
+ public static string FieldToString(byte[] bytes)
+ {
+ return FieldToString(bytes, String.Empty);
+ }
+
+ ///
+ /// Convert a variable length field (byte array) to a string, with a
+ /// field name prepended to each line of the output
+ ///
+ /// If the byte array has unprintable characters in it, a
+ /// hex dump will be put in the string instead
+ /// The byte array to convert to a string
+ /// A field name to prepend to each line of output
+ /// An ASCII string or a string containing a hex dump, minus
+ /// the null terminator
+ public static string FieldToString(byte[] bytes, string fieldName)
+ {
+ // Check for a common case
+ if (bytes.Length == 0) return String.Empty;
+
+ StringBuilder output = new StringBuilder();
+ bool printable = true;
+
+ for (int i = 0; i < bytes.Length; ++i)
+ {
+ // Check if there are any unprintable characters in the array
+ if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
+ && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
+ {
+ printable = false;
+ break;
+ }
+ }
+
+ if (printable)
+ {
+ if (fieldName.Length > 0)
+ {
+ output.Append(fieldName);
+ output.Append(": ");
+ }
+
+ if (bytes[bytes.Length - 1] == 0x00)
+ output.Append(UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1));
+ else
+ output.Append(UTF8Encoding.UTF8.GetString(bytes));
+ }
+ else
+ {
+ for (int i = 0; i < bytes.Length; i += 16)
+ {
+ if (i != 0)
+ output.Append(Environment.NewLine);
+ if (fieldName.Length > 0)
+ {
+ output.Append(fieldName);
+ output.Append(": ");
+ }
+
+ for (int j = 0; j < 16; j++)
+ {
+ if ((i + j) < bytes.Length)
+ output.Append(String.Format("{0:X2} ", bytes[i + j]));
+ else
+ output.Append(" ");
+ }
+
+ for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
+ {
+ if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
+ output.Append((char)bytes[i + j]);
+ else
+ output.Append(".");
+ }
+ }
+ }
+
+ return output.ToString();
+ }
+
+ ///
+ /// Converts a byte array to a string containing hexadecimal characters
+ ///
+ /// The byte array to convert to a string
+ /// The name of the field to prepend to each
+ /// line of the string
+ /// A string containing hexadecimal characters on multiple
+ /// lines. Each line is prepended with the field name
+ public static string FieldToHexString(byte[] bytes, string fieldName)
+ {
+ StringBuilder output = new StringBuilder();
+
+ for (int i = 0; i < bytes.Length; i += 16)
+ {
+ if (i != 0)
+ output.Append(Environment.NewLine);
+ if (fieldName.Length > 0)
+ output.Append(": ");
+
+ for (int j = 0; j < 16; j++)
+ {
+ if ((i + j) < bytes.Length)
+ output.Append(String.Format("{0:X2} ", bytes[i + j]));
+ else
+ output.Append(" ");
+ }
+
+ for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
+ {
+ if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
+ output.Append(bytes[i + j]);
+ else
+ output.Append(".");
+ }
+ }
+
+ return output.ToString();
+ }
+
+ /////
+ ///// Converts a string containing hexadecimal characters to a byte array
+ /////
+ ///// String containing hexadecimal characters
+ ///// The converted byte array
+ //public static byte[] HexStringToField(string hexString)
+ //{
+ // string newString = "";
+ // char c;
+
+ // // FIXME: For each line of the string, if a colon is found
+ // // remove everything before it
+
+ // // remove all non A-F, 0-9, characters
+ // for (int i = 0; i < hexString.Length; i++)
+ // {
+ // c = hexString[i];
+ // if (IsHexDigit(c))
+ // newString += c;
+ // }
+
+ // // if odd number of characters, discard last character
+ // if (newString.Length % 2 != 0)
+ // {
+ // newString = newString.Substring(0, newString.Length - 1);
+ // }
+
+ // int byteLength = newString.Length / 2;
+ // byte[] bytes = new byte[byteLength];
+ // string hex;
+ // int j = 0;
+ // for (int i = 0; i < bytes.Length; i++)
+ // {
+ // hex = new String(new Char[] { newString[j], newString[j + 1] });
+ // bytes[i] = HexToByte(hex);
+ // j = j + 2;
+ // }
+ // return bytes;
+ //}
+
+ ///
+ /// Convert a UTF8 string to a byte array
+ ///
+ /// The string to convert to a byte array
+ /// A null-terminated byte array
+ public static byte[] StringToField(string str)
+ {
+ if (str.Length == 0) { return new byte[0]; }
+ if (!str.EndsWith("\0")) { str += "\0"; }
+ return System.Text.UTF8Encoding.UTF8.GetBytes(str);
+ }
+
+ ///
+ /// Gets a unix timestamp for the current time
+ ///
+ /// An unsigned integer representing a unix timestamp for now
+ public static uint GetUnixTime()
+ {
+ return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
+ }
+
+ ///
+ /// Convert a unix timestamp to a native DateTime format
+ ///
+ /// An unsigned integer representing a unix
+ /// timestamp
+ /// A DateTime object containing the same time specified in
+ /// the given timestamp
+ public static DateTime UnixTimeToDateTime(uint timestamp)
+ {
+ // Make a DateTime equivalent to the UNIX Epoch
+ System.DateTime dateTime = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
+
+ // Add the number of seconds in our UNIX timestamp
+ dateTime = dateTime.AddSeconds(timestamp);
+
+ return dateTime;
+ }
+
+ ///
+ /// Converts a vector style rotation to a quaternion
+ ///
+ /// Axis rotation, such as 0,0,90 for 90 degrees to the right
+ /// A quaternion representing the axes of the supplied vector
+ public static LLQuaternion Axis2Rot(LLVector3 a)
+ {
+ if (a.X > 180) a.X -= 360; if (a.Y > 180) a.Y -= 360; if (a.Z > 180) a.Z -= 360;
+ if (a.X < -180) a.X += 360; if (a.Y < -180) a.Y += 360; if (a.Z < -180) a.Z += 360;
+
+ LLQuaternion rot = LLQuaternion.Identity;
+ rot.X = (float)(a.X * DEG_TO_RAD);
+ rot.Y = (float)(a.Y * DEG_TO_RAD);
+ rot.Z = (float)(a.Z * DEG_TO_RAD);
+ if (a.Z > 180) rot.W = 0;
+
+ return rot;
+ }
+
+ ///
+ /// Calculates the distance between two vectors
+ ///
+ public static float VecDist(LLVector3 pointA, LLVector3 pointB)
+ {
+ float xd = pointB.X - pointA.X;
+ float yd = pointB.Y - pointA.Y;
+ float zd = pointB.Z - pointA.Z;
+ return (float)Math.Sqrt(xd * xd + yd * yd + zd * zd);
+ }
+
+ ///
+ /// Calculate the magnitude of the supplied vector
+ ///
+ public static float VecMag(LLVector3 v)
+ {
+ return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
+ }
+
+ ///
+ /// Calculate the magnitude of the supplied quaternion
+ ///
+ public static float RotMag(LLQuaternion q)
+ {
+ return (float)Math.Sqrt(q.W * q.W + q.X * q.X + q.Y * q.Y + q.Z * q.Z);
+ }
+
+ ///
+ /// Return the supplied vector in normalized form
+ ///
+ public static LLVector3 VecNorm(LLVector3 vector)
+ {
+ float mag = VecMag(vector);
+ return new LLVector3(vector.X / mag, vector.Y / mag, vector.Z / mag);
+ }
+
+ ///
+ /// Calculate the rotation between two vectors
+ ///
+ /// Directional vector, such as 1,0,0 for the forward face
+ /// Target vector - normalize first with VecNorm
+ public static LLQuaternion RotBetween(LLVector3 a, LLVector3 b)
+ {
+ //A and B should both be normalized
+ //dotProduct is 0 if a and b are perpendicular. I think that's normal?
+ float dotProduct = (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z);
+
+ LLVector3 crossProduct = new LLVector3();
+ crossProduct.X = a.Y * b.Z - a.Z * b.Y;
+ crossProduct.Y = a.Z * b.X - a.X * b.Z;
+ crossProduct.Z = a.X * b.Y - a.Y * b.X;
+
+ //float scalarProduct = (a.X * b.Y) + (a.Y * b.Z) + (a.Z * b.X); //not used?
+ float magProduct = VecMag(a) * VecMag(b);
+ double angle = Math.Acos(dotProduct / magProduct);
+
+ LLVector3 axis = VecNorm(crossProduct);
+ float s = (float)Math.Sin(angle / 2);
+ return new LLQuaternion(axis.X * s, axis.Y * s, axis.Z * s, (float)Math.Cos(angle / 2));
+ }
+
+ ///
+ /// Decode a zerocoded byte array, used to decompress packets marked
+ /// with the zerocoded flag
+ ///
+ /// Any time a zero is encountered, the next byte is a count
+ /// of how many zeroes to expand. One zero is encoded with 0x00 0x01,
+ /// two zeroes is 0x00 0x02, three zeroes is 0x00 0x03, etc. The
+ /// first four bytes are copied directly to the output buffer.
+ ///
+ /// The byte array to decode
+ /// The length of the byte array to decode. This
+ /// would be the length of the packet up to (but not including) any
+ /// appended ACKs
+ /// The output byte array to decode to
+ /// The length of the output buffer
+ public static int ZeroDecode(byte[] src, int srclen, byte[] dest)
+ {
+ uint zerolen = 0;
+ int bodylen = 0;
+ uint i = 0;
+
+ try
+ {
+ Array.Copy(src, 0, dest, 0, 4);
+ zerolen = 4;
+ bodylen = srclen;
+
+ for (i = zerolen; i < bodylen; i++)
+ {
+ if (src[i] == 0x00)
+ {
+ for (byte j = 0; j < src[i + 1]; j++)
+ {
+ dest[zerolen++] = 0x00;
+ }
+
+ i++;
+ }
+ else
+ {
+ dest[zerolen++] = src[i];
+ }
+ }
+
+ // Copy appended ACKs
+ for (; i < srclen; i++)
+ {
+ dest[zerolen++] = src[i];
+ }
+
+ return (int)zerolen;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Zerodecoding error: " + Environment.NewLine +
+ "i=" + i + "srclen=" + srclen + ", bodylen=" + bodylen + ", zerolen=" + zerolen + Environment.NewLine +
+ FieldToString(src, "src") + Environment.NewLine +
+ e.ToString());
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Decode enough of a byte array to get the packet ID. Data before and
+ /// after the packet ID is undefined.
+ ///
+ /// The byte array to decode
+ /// The output byte array to encode to
+ public static void ZeroDecodeCommand(byte[] src, byte[] dest)
+ {
+ for (int srcPos = 4, destPos = 4; destPos < 8; ++srcPos)
+ {
+ if (src[srcPos] == 0x00)
+ {
+ for (byte j = 0; j < src[srcPos + 1]; ++j)
+ {
+ dest[destPos++] = 0x00;
+ }
+
+ ++srcPos;
+ }
+ else
+ {
+ dest[destPos++] = src[srcPos];
+ }
+ }
+ }
+
+ ///
+ /// Encode a byte array with zerocoding. Used to compress packets marked
+ /// with the zerocoded flag. Any zeroes in the array are compressed down
+ /// to a single zero byte followed by a count of how many zeroes to expand
+ /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02,
+ /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied
+ /// directly to the output buffer.
+ ///
+ /// The byte array to encode
+ /// The length of the byte array to encode
+ /// The output byte array to encode to
+ /// The length of the output buffer
+ public static int ZeroEncode(byte[] src, int srclen, byte[] dest)
+ {
+ uint zerolen = 0;
+ byte zerocount = 0;
+
+ Array.Copy(src, 0, dest, 0, 4);
+ zerolen += 4;
+
+ int bodylen;
+ if ((src[0] & MSG_APPENDED_ACKS) == 0)
+ {
+ bodylen = srclen;
+ }
+ else
+ {
+ bodylen = srclen - src[srclen - 1] * 4 - 1;
+ }
+
+ uint i;
+ for (i = zerolen; i < bodylen; i++)
+ {
+ if (src[i] == 0x00)
+ {
+ zerocount++;
+
+ if (zerocount == 0)
+ {
+ dest[zerolen++] = 0x00;
+ dest[zerolen++] = 0xff;
+ zerocount++;
+ }
+ }
+ else
+ {
+ if (zerocount != 0)
+ {
+ dest[zerolen++] = 0x00;
+ dest[zerolen++] = (byte)zerocount;
+ zerocount = 0;
+ }
+
+ dest[zerolen++] = src[i];
+ }
+ }
+
+ if (zerocount != 0)
+ {
+ dest[zerolen++] = 0x00;
+ dest[zerolen++] = (byte)zerocount;
+ }
+
+ // copy appended ACKs
+ for (; i < srclen; i++)
+ {
+ dest[zerolen++] = src[i];
+ }
+
+ return (int)zerolen;
+ }
+
+ ///
+ /// Calculates the CRC (cyclic redundancy check) needed to upload inventory.
+ ///
+ /// Creation date
+ /// Sale type
+ /// Inventory type
+ /// Type
+ /// Asset ID
+ /// Group ID
+ /// Sale price
+ /// Owner ID
+ /// Creator ID
+ /// Item ID
+ /// Folder ID
+ /// Everyone mask (permissions)
+ /// Flags
+ /// Next owner mask (permissions)
+ /// Group mask (permissions)
+ /// Owner mask (permisions)
+ /// The calculated CRC
+ public static uint InventoryCRC(int creationDate, byte saleType, sbyte invType, sbyte type,
+ LLUUID assetID, LLUUID groupID, int salePrice, LLUUID ownerID, LLUUID creatorID,
+ LLUUID itemID, LLUUID folderID, uint everyoneMask, uint flags, uint nextOwnerMask,
+ uint groupMask, uint ownerMask)
+ {
+ uint CRC = 0;
+
+ // IDs
+ CRC += assetID.CRC(); // AssetID
+ CRC += folderID.CRC(); // FolderID
+ CRC += itemID.CRC(); // ItemID
+
+ // Permission stuff
+ CRC += creatorID.CRC(); // CreatorID
+ CRC += ownerID.CRC(); // OwnerID
+ CRC += groupID.CRC(); // GroupID
+
+ // CRC += another 4 words which always seem to be zero -- unclear if this is a LLUUID or what
+ CRC += ownerMask;
+ CRC += nextOwnerMask;
+ CRC += everyoneMask;
+ CRC += groupMask;
+
+ // The rest of the CRC fields
+ CRC += flags; // Flags
+ CRC += (uint)invType; // InvType
+ CRC += (uint)type; // Type
+ CRC += (uint)creationDate; // CreationDate
+ CRC += (uint)salePrice; // SalePrice
+ CRC += (uint)((uint)saleType * 0x07073096); // SaleType
+
+ return CRC;
+ }
+
+ ///
+ /// Calculate the MD5 hash of a given string
+ ///
+ /// The password to hash
+ /// An MD5 hash in string format, with $1$ prepended
+ public static string MD5(string password)
+ {
+ StringBuilder digest = new StringBuilder();
+ byte[] hash = MD5Builder.ComputeHash(ASCIIEncoding.Default.GetBytes(password));
+
+ // Convert the hash to a hex string
+ foreach (byte b in hash)
+ {
+ digest.AppendFormat("{0:x2}", b);
+ }
+
+ return "$1$" + digest.ToString();
+ }
+
+ public static void PacketListToXml(List packets, XmlWriter xmlWriter)
+ {
+ //XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
+ //ns.Add("", "");
+ XmlSerializer serializer = new XmlSerializer(typeof(List));
+ serializer.Serialize(xmlWriter, packets);
+ }
+
+ public static void PrimListToXml(List list, XmlWriter xmlWriter)
+ {
+ XmlSerializer serializer = new XmlSerializer(typeof(List));
+ serializer.Serialize(xmlWriter, list);
+ }
+
+ public static List PrimListFromXml(XmlReader reader)
+ {
+ XmlSerializer serializer = new XmlSerializer(typeof(List));
+ object list = serializer.Deserialize(reader);
+ return (List)list;
+ }
+
+ public static List PacketListFromXml(XmlReader reader)
+ {
+ XmlSerializer serializer = new XmlSerializer(typeof(List));
+ object list = serializer.Deserialize(reader);
+ return (List)list;
+ }
+ }
+}
diff --git a/libsecondlife-cs/LLObject.cs b/libsecondlife-cs/LLObject.cs
index 6da3aea6..fdc02a6e 100644
--- a/libsecondlife-cs/LLObject.cs
+++ b/libsecondlife-cs/LLObject.cs
@@ -1,602 +1,602 @@
-/*
- * Copyright (c) 2007, 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 System.Xml;
-using System.Xml.Serialization;
-using libsecondlife.Packets;
-
-namespace libsecondlife
-{
- ///
- /// Base class for primitives and avatars
- ///
- [Serializable]
- public abstract partial class LLObject
- {
- #region Enumerations
-
- ///
- /// Primary parameters for primitives such as Physics Enabled or Phantom
- ///
- [Flags]
- public enum ObjectFlags : uint
- {
- /// None of the primary flags are enabled
- None = 0,
- /// Whether physics are enabled for this object
- Physics = 0x00000001,
- ///
- CreateSelected = 0x00000002,
- ///
- ObjectModify = 0x00000004,
- ///
- ObjectCopy = 0x00000008,
- ///
- ObjectAnyOwner = 0x00000010,
- ///
- ObjectYouOwner = 0x00000020,
- ///
- Scripted = 0x00000040,
- /// Whether this object contains an active touch script
- Touch = 0x00000080,
- ///
- ObjectMove = 0x00000100,
- /// Whether this object can receive payments
- Money = 0x00000200,
- /// Whether this object is phantom (no collisions)
- Phantom = 0x00000400,
- ///
- InventoryEmpty = 0x00000800,
- ///
- JointHinge = 0x00001000,
- ///
- JointP2P = 0x00002000,
- ///
- JointLP2P = 0x00004000,
- /// Deprecated
- JointWheel = 0x00008000,
- ///
- AllowInventoryDrop = 0x00010000,
- ///
- ObjectTransfer = 0x00020000,
- ///
- ObjectGroupOwned = 0x00040000,
- /// Deprecated
- ObjectYouOfficer = 0x00080000,
- ///
- CameraDecoupled = 0x00100000,
- ///
- AnimSource = 0x00200000,
- ///
- CameraSource = 0x00400000,
- ///
- CastShadows = 0x00800000,
- ///
- ObjectOwnerModify = 0x10000000,
- ///
- TemporaryOnRez = 0x20000000,
- ///
- Temporary = 0x40000000,
- ///
- ZlibCompressed = 0x80000000
- }
-
- #endregion Enumerations
-
-
- #region Structs
-
- ///
- ///
- ///
- [Serializable]
- public struct ObjectData
- {
- ///
- [XmlAttribute("pathtwistbegin")]
- public int PathTwistBegin;
- ///
- [XmlAttribute("pathend")]
- public float PathEnd;
- ///
- [XmlAttribute("profilebegin")]
- public float ProfileBegin;
- ///
- [XmlAttribute("pathradiusoffset")]
- public float PathRadiusOffset;
- ///
- [XmlAttribute("pathskew")]
- public float PathSkew;
- ///
- [XmlAttribute("profilecurve")]
- public uint ProfileCurve;
- ///
- [XmlAttribute("pathscalex")]
- public float PathScaleX;
- ///
- [XmlAttribute("pathscaley")]
- public float PathScaleY;
- ///
- [XmlAttribute("material")]
- public uint Material;
- ///
- [XmlAttribute("pathshearx")]
- public float PathShearX;
- ///
- [XmlAttribute("pathsheary")]
- public float PathShearY;
- ///
- [XmlAttribute("pathtaperx")]
- public float PathTaperX;
- ///
- [XmlAttribute("pathtapery")]
- public float PathTaperY;
- ///
- [XmlAttribute("profileend")]
- public float ProfileEnd;
- ///
- [XmlAttribute("pathbegin")]
- public float PathBegin;
- ///
- [XmlAttribute("pathcurve")]
- public uint PathCurve;
- ///
- [XmlAttribute("pathtwist")]
- public int PathTwist;
- ///
- [XmlAttribute("profilehollow")]
- public uint ProfileHollow;
- ///
- [XmlAttribute("pathrevolutions")]
- public float PathRevolutions;
- ///
- [XmlAttribute("state")]
- public uint State;
- ///
- [XmlIgnore]
- public ObjectManager.PCode PCode;
- }
-
- ///
- ///
- ///
- public struct ObjectProperties
- {
- ///
- public LLUUID ObjectID;
- ///
- public LLUUID CreatorID;
- ///
- public LLUUID OwnerID;
- ///
- public LLUUID GroupID;
- ///
- public ulong CreationDate;
- ///
- public uint BaseMask;
- ///
- public uint OwnerMask;
- ///
- public uint GroupMask;
- ///
- public uint EveryoneMask;
- ///
- public uint NextOwnerMask;
- ///
- public int OwnershipCost;
- ///
- public byte SaleType;
- ///
- public int SalePrice;
- ///
- public byte AggregatePerms;
- ///
- public byte AggregatePermTextures;
- ///
- public byte AggregatePermTexturesOwner;
- ///
- public uint Category;
- ///
- public short InventorySerial;
- ///
- public LLUUID ItemID;
- ///
- public LLUUID FolderID;
- ///
- public LLUUID FromTaskID;
- ///
- public LLUUID LastOwnerID;
- ///
- public string Name;
- ///
- public string Description;
- ///
- public string TouchName;
- ///
- public string SitName;
- ///
- public LLUUID[] TextureIDs;
- }
-
- ///
- ///
- ///
- public struct ObjectPropertiesFamily
- {
- ///
- ///
- ///
- public enum RequestFlagsType
- {
- ///
- BugReportRequest = 1,
- ///
- ComplaintReportRequest = 2
- }
-
- ///
- public RequestFlagsType RequestFlags;
- ///
- public LLUUID ObjectID;
- ///
- public LLUUID OwnerID;
- ///
- public LLUUID GroupID;
- ///
- public uint BaseMask;
- ///
- public uint OwnerMask;
- ///
- public uint GroupMask;
- ///
- public uint EveryoneMask;
- ///
- public uint NextOwnerMask;
- ///
- public int OwnershipCost;
- ///
- public byte SaleType;
- ///
- public int SalePrice;
- ///
- public uint Category;
- ///
- public LLUUID LastOwnerID;
- ///
- public string Name;
- ///
- public string Description;
- }
-
- #endregion Structs
-
-
- #region Public Members
-
- ///
- public LLUUID ID = LLUUID.Zero;
- ///
- public LLUUID GroupID = LLUUID.Zero;
- ///
- public uint LocalID;
- ///
- public uint ParentID;
- ///
- public ulong RegionHandle;
- ///
- public ObjectFlags Flags;
- /// Unknown
- public byte[] GenericData;
- ///
- public LLVector3 Position;
- ///
- public LLVector3 Scale;
- ///
- public LLQuaternion Rotation = LLQuaternion.Identity;
- ///
- public LLVector3 Velocity;
- ///
- public LLVector3 AngularVelocity;
- ///
- public LLVector3 Acceleration;
- ///
- public LLVector4 CollisionPlane;
- ///
- public TextureEntry Textures;
- ///
- public ObjectProperties Properties;
- ///
- public ObjectPropertiesFamily PropertiesFamily;
- ///
- public SerializableDictionary NameValues = new SerializableDictionary();
-
- #endregion Public Members
-
-
- #region Public Properties
-
- ///
- public ObjectData Data { get { return data; } }
-
- #endregion Public Properties
-
-
- internal ObjectData data = new ObjectData();
-
-
- public override bool Equals(object obj)
- {
- LLObject llobj = obj as LLObject;
- if (llobj == null)
- return false;
- return ID.Equals(llobj.ID);
- }
-
- public override int GetHashCode()
- {
- return ID.GetHashCode();
- }
-
- #region Static Methods
-
- ///
- ///
- ///
- ///
- ///
- public static byte PathScaleByte(float pathScale)
- {
- // Y = 100 + 100X
- int scale = (int)Math.Round(100.0f * pathScale);
- return (byte)(100 + scale);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathScaleFloat(byte pathScale)
- {
- // Y = -1 + 0.01X
- return (float)Math.Round((double)pathScale * 0.01d - 1.0d, 6);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte PathShearByte(float pathShear)
- {
- // Y = 256 + 100X
- int shear = (int)Math.Round(100.0f * pathShear);
- shear += 256;
- return (byte)(shear % 256);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathShearFloat(byte pathShear)
- {
- if (pathShear == 0) return 0.0f;
-
- if (pathShear > 150)
- {
- // Negative value
- return ((float)pathShear - 256.0f) / 100.0f;
- }
- else
- {
- // Positive value
- return (float)pathShear / 100.0f;
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte ProfileBeginByte(float profileBegin)
- {
- // Y = ceil (200X)
- return (byte)Math.Round(200.0f * profileBegin);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float ProfileBeginFloat(byte profileBegin)
- {
- // Y = 0.005X
- return (float)Math.Round((double)profileBegin * 0.005d, 6);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte ProfileEndByte(float profileEnd)
- {
- // Y = 200 - 200X
- int end = (int)Math.Round(200.0d * (double)profileEnd);
- return (byte)(200 - end);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float ProfileEndFloat(byte profileEnd)
- {
- // Y = 1 - 0.005X
- return (float)Math.Round(1.0d - ((double)profileEnd * 0.005d), 6);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte PathBeginByte(float pathBegin)
- {
- // Y = 100X
- return (byte)Convert.ToInt16(100.0f * pathBegin);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathBeginFloat(byte pathBegin)
- {
- // Y = X / 100
- return (float)pathBegin / 100.0f;
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte PathEndByte(float pathEnd)
- {
- // Y = 100 - 100X
- int end = (int)Math.Round(100.0f * pathEnd);
- return (byte)(100 - end);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathEndFloat(byte pathEnd)
- {
- // Y = 1 - X / 100
- return 1.0f - (float)pathEnd / 100.0f;
- }
-
- ///
- ///
- ///
- ///
- ///
- public static sbyte PathRadiusOffsetByte(float pathRadiusOffset)
- {
- // Y = 256 + 100X
- return (sbyte)PathShearByte(pathRadiusOffset);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathRadiusOffsetFloat(sbyte pathRadiusOffset)
- {
- // Y = X / 100
- return (float)pathRadiusOffset / 100.0f;
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte PathRevolutionsByte(float pathRevolutions)
- {
- // Y = 66.5X - 66
- int revolutions = (int)Math.Round(66.5d * (double)pathRevolutions);
- return (byte)(revolutions - 66);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathRevolutionsFloat(byte pathRevolutions)
- {
- // Y = 1 + 0.015X
- return (float)Math.Round(1.0d + (double)pathRevolutions * 0.015d, 6);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static sbyte PathSkewByte(float pathSkew)
- {
- return PathTaperByte(pathSkew);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathSkewFloat(sbyte pathSkew)
- {
- return PathTaperFloat(pathSkew);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static sbyte PathTaperByte(float pathTaper)
- {
- // Y = 256 + 100X
- return (sbyte)PathShearByte(pathTaper);
- }
-
- ///
- ///
- ///
- ///
- ///
- public static float PathTaperFloat(sbyte pathTaper)
- {
- return (float)pathTaper / 100.0f;
- }
-
- #endregion Static Methods
- }
-}
+/*
+ * Copyright (c) 2007, 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 System.Xml;
+using System.Xml.Serialization;
+using libsecondlife.Packets;
+
+namespace libsecondlife
+{
+ ///
+ /// Base class for primitives and avatars
+ ///
+ [Serializable]
+ public abstract partial class LLObject
+ {
+ #region Enumerations
+
+ ///
+ /// Primary parameters for primitives such as Physics Enabled or Phantom
+ ///
+ [Flags]
+ public enum ObjectFlags : uint
+ {
+ /// None of the primary flags are enabled
+ None = 0,
+ /// Whether physics are enabled for this object
+ Physics = 0x00000001,
+ ///
+ CreateSelected = 0x00000002,
+ ///
+ ObjectModify = 0x00000004,
+ ///
+ ObjectCopy = 0x00000008,
+ ///
+ ObjectAnyOwner = 0x00000010,
+ ///
+ ObjectYouOwner = 0x00000020,
+ ///
+ Scripted = 0x00000040,
+ /// Whether this object contains an active touch script
+ Touch = 0x00000080,
+ ///
+ ObjectMove = 0x00000100,
+ /// Whether this object can receive payments
+ Money = 0x00000200,
+ /// Whether this object is phantom (no collisions)
+ Phantom = 0x00000400,
+ ///
+ InventoryEmpty = 0x00000800,
+ ///
+ JointHinge = 0x00001000,
+ ///
+ JointP2P = 0x00002000,
+ ///
+ JointLP2P = 0x00004000,
+ /// Deprecated
+ JointWheel = 0x00008000,
+ ///
+ AllowInventoryDrop = 0x00010000,
+ ///
+ ObjectTransfer = 0x00020000,
+ ///
+ ObjectGroupOwned = 0x00040000,
+ /// Deprecated
+ ObjectYouOfficer = 0x00080000,
+ ///
+ CameraDecoupled = 0x00100000,
+ ///
+ AnimSource = 0x00200000,
+ ///
+ CameraSource = 0x00400000,
+ ///
+ CastShadows = 0x00800000,
+ ///
+ ObjectOwnerModify = 0x10000000,
+ ///
+ TemporaryOnRez = 0x20000000,
+ ///
+ Temporary = 0x40000000,
+ ///
+ ZlibCompressed = 0x80000000
+ }
+
+ #endregion Enumerations
+
+
+ #region Structs
+
+ ///
+ ///
+ ///
+ [Serializable]
+ public struct ObjectData
+ {
+ ///
+ [XmlAttribute("pathtwistbegin")]
+ public int PathTwistBegin;
+ ///
+ [XmlAttribute("pathend")]
+ public float PathEnd;
+ ///
+ [XmlAttribute("profilebegin")]
+ public float ProfileBegin;
+ ///
+ [XmlAttribute("pathradiusoffset")]
+ public float PathRadiusOffset;
+ ///
+ [XmlAttribute("pathskew")]
+ public float PathSkew;
+ ///
+ [XmlAttribute("profilecurve")]
+ public uint ProfileCurve;
+ ///
+ [XmlAttribute("pathscalex")]
+ public float PathScaleX;
+ ///
+ [XmlAttribute("pathscaley")]
+ public float PathScaleY;
+ ///
+ [XmlAttribute("material")]
+ public uint Material;
+ ///
+ [XmlAttribute("pathshearx")]
+ public float PathShearX;
+ ///
+ [XmlAttribute("pathsheary")]
+ public float PathShearY;
+ ///
+ [XmlAttribute("pathtaperx")]
+ public float PathTaperX;
+ ///
+ [XmlAttribute("pathtapery")]
+ public float PathTaperY;
+ ///
+ [XmlAttribute("profileend")]
+ public float ProfileEnd;
+ ///
+ [XmlAttribute("pathbegin")]
+ public float PathBegin;
+ ///
+ [XmlAttribute("pathcurve")]
+ public uint PathCurve;
+ ///
+ [XmlAttribute("pathtwist")]
+ public int PathTwist;
+ ///
+ [XmlAttribute("profilehollow")]
+ public uint ProfileHollow;
+ ///
+ [XmlAttribute("pathrevolutions")]
+ public float PathRevolutions;
+ ///
+ [XmlAttribute("state")]
+ public uint State;
+ ///
+ [XmlIgnore]
+ public ObjectManager.PCode PCode;
+ }
+
+ ///
+ ///
+ ///
+ public struct ObjectProperties
+ {
+ ///
+ public LLUUID ObjectID;
+ ///
+ public LLUUID CreatorID;
+ ///
+ public LLUUID OwnerID;
+ ///
+ public LLUUID GroupID;
+ ///
+ public ulong CreationDate;
+ ///
+ public uint BaseMask;
+ ///
+ public uint OwnerMask;
+ ///
+ public uint GroupMask;
+ ///
+ public uint EveryoneMask;
+ ///
+ public uint NextOwnerMask;
+ ///
+ public int OwnershipCost;
+ ///
+ public byte SaleType;
+ ///
+ public int SalePrice;
+ ///
+ public byte AggregatePerms;
+ ///
+ public byte AggregatePermTextures;
+ ///
+ public byte AggregatePermTexturesOwner;
+ ///
+ public uint Category;
+ ///
+ public short InventorySerial;
+ ///
+ public LLUUID ItemID;
+ ///
+ public LLUUID FolderID;
+ ///
+ public LLUUID FromTaskID;
+ ///
+ public LLUUID LastOwnerID;
+ ///
+ public string Name;
+ ///
+ public string Description;
+ ///
+ public string TouchName;
+ ///
+ public string SitName;
+ ///
+ public LLUUID[] TextureIDs;
+ }
+
+ ///
+ ///
+ ///
+ public struct ObjectPropertiesFamily
+ {
+ ///
+ ///
+ ///
+ public enum RequestFlagsType
+ {
+ ///
+ BugReportRequest = 1,
+ ///
+ ComplaintReportRequest = 2
+ }
+
+ ///
+ public RequestFlagsType RequestFlags;
+ ///
+ public LLUUID ObjectID;
+ ///
+ public LLUUID OwnerID;
+ ///
+ public LLUUID GroupID;
+ ///
+ public uint BaseMask;
+ ///
+ public uint OwnerMask;
+ ///
+ public uint GroupMask;
+ ///
+ public uint EveryoneMask;
+ ///
+ public uint NextOwnerMask;
+ ///
+ public int OwnershipCost;
+ ///
+ public byte SaleType;
+ ///
+ public int SalePrice;
+ ///
+ public uint Category;
+ ///
+ public LLUUID LastOwnerID;
+ ///
+ public string Name;
+ ///
+ public string Description;
+ }
+
+ #endregion Structs
+
+
+ #region Public Members
+
+ ///
+ public LLUUID ID = LLUUID.Zero;
+ ///
+ public LLUUID GroupID = LLUUID.Zero;
+ ///
+ public uint LocalID;
+ ///
+ public uint ParentID;
+ ///
+ public ulong RegionHandle;
+ ///
+ public ObjectFlags Flags;
+ /// Unknown
+ public byte[] GenericData;
+ ///
+ public LLVector3 Position;
+ ///
+ public LLVector3 Scale;
+ ///
+ public LLQuaternion Rotation = LLQuaternion.Identity;
+ ///
+ public LLVector3 Velocity;
+ ///
+ public LLVector3 AngularVelocity;
+ ///
+ public LLVector3 Acceleration;
+ ///
+ public LLVector4 CollisionPlane;
+ ///
+ public TextureEntry Textures;
+ ///
+ public ObjectProperties Properties;
+ ///
+ public ObjectPropertiesFamily PropertiesFamily;
+ ///
+ public SerializableDictionary NameValues = new SerializableDictionary();
+
+ #endregion Public Members
+
+
+ #region Public Properties
+
+ ///
+ public ObjectData Data { get { return data; } }
+
+ #endregion Public Properties
+
+
+ internal ObjectData data = new ObjectData();
+
+
+ public override bool Equals(object obj)
+ {
+ LLObject llobj = obj as LLObject;
+ if (llobj == null)
+ return false;
+ return ID.Equals(llobj.ID);
+ }
+
+ public override int GetHashCode()
+ {
+ return ID.GetHashCode();
+ }
+
+ #region Static Methods
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte PathScaleByte(float pathScale)
+ {
+ // Y = 100 + 100X
+ int scale = (int)Math.Round(100.0f * pathScale);
+ return (byte)(100 + scale);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathScaleFloat(byte pathScale)
+ {
+ // Y = -1 + 0.01X
+ return (float)Math.Round((double)pathScale * 0.01d - 1.0d, 6);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte PathShearByte(float pathShear)
+ {
+ // Y = 256 + 100X
+ int shear = (int)Math.Round(100.0f * pathShear);
+ shear += 256;
+ return (byte)(shear % 256);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathShearFloat(byte pathShear)
+ {
+ if (pathShear == 0) return 0.0f;
+
+ if (pathShear > 150)
+ {
+ // Negative value
+ return ((float)pathShear - 256.0f) / 100.0f;
+ }
+ else
+ {
+ // Positive value
+ return (float)pathShear / 100.0f;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte ProfileBeginByte(float profileBegin)
+ {
+ // Y = ceil (200X)
+ return (byte)Math.Round(200.0f * profileBegin);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float ProfileBeginFloat(byte profileBegin)
+ {
+ // Y = 0.005X
+ return (float)Math.Round((double)profileBegin * 0.005d, 6);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte ProfileEndByte(float profileEnd)
+ {
+ // Y = 200 - 200X
+ int end = (int)Math.Round(200.0d * (double)profileEnd);
+ return (byte)(200 - end);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float ProfileEndFloat(byte profileEnd)
+ {
+ // Y = 1 - 0.005X
+ return (float)Math.Round(1.0d - ((double)profileEnd * 0.005d), 6);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte PathBeginByte(float pathBegin)
+ {
+ // Y = 100X
+ return (byte)Convert.ToInt16(100.0f * pathBegin);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathBeginFloat(byte pathBegin)
+ {
+ // Y = X / 100
+ return (float)pathBegin / 100.0f;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte PathEndByte(float pathEnd)
+ {
+ // Y = 100 - 100X
+ int end = (int)Math.Round(100.0f * pathEnd);
+ return (byte)(100 - end);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathEndFloat(byte pathEnd)
+ {
+ // Y = 1 - X / 100
+ return 1.0f - (float)pathEnd / 100.0f;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static sbyte PathRadiusOffsetByte(float pathRadiusOffset)
+ {
+ // Y = 256 + 100X
+ return (sbyte)PathShearByte(pathRadiusOffset);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathRadiusOffsetFloat(sbyte pathRadiusOffset)
+ {
+ // Y = X / 100
+ return (float)pathRadiusOffset / 100.0f;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte PathRevolutionsByte(float pathRevolutions)
+ {
+ // Y = 66.5X - 66
+ int revolutions = (int)Math.Round(66.5d * (double)pathRevolutions);
+ return (byte)(revolutions - 66);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathRevolutionsFloat(byte pathRevolutions)
+ {
+ // Y = 1 + 0.015X
+ return (float)Math.Round(1.0d + (double)pathRevolutions * 0.015d, 6);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static sbyte PathSkewByte(float pathSkew)
+ {
+ return PathTaperByte(pathSkew);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathSkewFloat(sbyte pathSkew)
+ {
+ return PathTaperFloat(pathSkew);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static sbyte PathTaperByte(float pathTaper)
+ {
+ // Y = 256 + 100X
+ return (sbyte)PathShearByte(pathTaper);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static float PathTaperFloat(sbyte pathTaper)
+ {
+ return (float)pathTaper / 100.0f;
+ }
+
+ #endregion Static Methods
+ }
+}
diff --git a/libsecondlife-cs/LLSD.cs b/libsecondlife-cs/LLSD.cs
index f7a7d1a7..7fb5c040 100644
--- a/libsecondlife-cs/LLSD.cs
+++ b/libsecondlife-cs/LLSD.cs
@@ -1,449 +1,449 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Xml;
-using System.IO;
-using libsecondlife;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace libsecondlife
-{
- ///
- ///
- ///
- public class LLSD
- {
- ///
- ///
- ///
- public class LLSDParseException : Exception
- {
- public LLSDParseException(string message) : base(message) { }
- }
-
- ///
- ///
- ///
- public class LLSDSerializeException : Exception
- {
- public LLSDSerializeException(string message) : base(message) { }
- }
-
- ///
- ///
- ///
- ///
- ///
- public static object LLSDDeserialize(byte[] b)
- {
- return LLSDDeserialize(new MemoryStream(b, false));
- }
-
- ///
- ///
- ///
- ///
- ///
- public static object LLSDDeserialize(Stream st)
- {
- XmlTextReader reader = new XmlTextReader(st);
- reader.Read(); SkipWS(reader);
- if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "llsd")
- {
- throw new LLSDParseException("Expected ");
- }
- reader.Read();
- object ret = LLSDParseOne(reader);
- SkipWS(reader);
- if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "llsd")
- throw new LLSDParseException("Expected ");
- return ret;
- }
-
- ///
- ///
- ///
- ///
- ///
- public static byte[] LLSDSerialize(object obj)
- {
- StringWriter sw = new StringWriter();
- XmlTextWriter writer = new XmlTextWriter(sw);
- writer.Formatting = Formatting.None;
- writer.WriteStartElement("", "llsd", "");
- LLSDWriteOne(writer, obj);
- writer.WriteEndElement();
- writer.Close();
- return Encoding.UTF8.GetBytes(sw.ToString());
- }
-
- ///
- ///
- ///
- ///
- ///
- public static void LLSDWriteOne(XmlTextWriter writer, object obj)
- {
- if (obj == null)
- {
- writer.WriteStartElement("", "undef", "");
- writer.WriteEndElement();
- return;
- }
-
- Type t = obj.GetType();
- if (t == typeof(string))
- {
- writer.WriteStartElement("", "string", "");
- writer.WriteString((string)obj);
- writer.WriteEndElement();
- }
- else if (t == typeof(long))
- {
- writer.WriteStartElement("", "integer", "");
- writer.WriteString(obj.ToString());
- writer.WriteEndElement();
- }
- else if (t == typeof(double))
- {
- writer.WriteStartElement("", "real", "");
- writer.WriteString(obj.ToString());
- writer.WriteEndElement();
- }
- else if (t == typeof(bool))
- {
- bool b = (bool)obj;
- writer.WriteStartElement("", "boolean", "");
- if (b)
- writer.WriteString("1");
- else writer.WriteString("0");
- writer.WriteEndElement();
- }
- else if (t == typeof(LLUUID))
- {
- LLUUID u = (LLUUID)obj;
- writer.WriteStartElement("", "uuid", "");
- writer.WriteString(u.ToStringHyphenated());
- writer.WriteEndElement();
- }
- else if (t == typeof(Hashtable))
- {
- Hashtable h = (Hashtable)obj;
- writer.WriteStartElement("", "map", "");
- foreach (string key in h.Keys)
- {
- writer.WriteStartElement("", "key", "");
- writer.WriteString(key);
- writer.WriteEndElement();
- LLSDWriteOne(writer, h[key]);
- }
- writer.WriteEndElement();
- }
- else if (t == typeof(ArrayList))
- {
- ArrayList a = (ArrayList)obj;
- writer.WriteStartElement("", "array", "");
- foreach (object item in a)
- {
- LLSDWriteOne(writer, item);
- }
- writer.WriteEndElement();
- }
- else if (t == typeof(byte[]))
- {
- byte[] b = (byte[])obj;
- writer.WriteStartElement("", "binary", "");
- writer.WriteStartAttribute("", "encoding", "");
- writer.WriteString("base64");
- writer.WriteEndAttribute();
- char[] tmp = new char[b.Length * 2]; // too much
- int i = Convert.ToBase64CharArray(b, 0, b.Length, tmp, 0);
- Array.Resize(ref tmp, i);
- writer.WriteString(new String(tmp));
- writer.WriteEndElement();
-
- }
- else
- {
- throw new LLSDSerializeException("Unknown type " + t.Name);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public static object LLSDParseOne(XmlTextReader reader)
- {
- SkipWS(reader);
- if (reader.NodeType != XmlNodeType.Element)
- throw new LLSDParseException("Expected an element");
- string dtype = reader.LocalName; object ret = null;
- //bool st = false;
-
- switch (dtype)
- {
- case "undef":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return null;
- }
- reader.Read(); SkipWS(reader); ret = null; break;
- }
- case "boolean":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return false;
- }
- reader.Read();
- string s = reader.ReadString().Trim();
- if (s == "" || s == "false" || s == "0")
- {
- ret = false;
- }
- else if (s == "true" || s == "1")
- {
- ret = true;
- }
- else
- {
- throw new LLSDParseException("Bad boolean value " + s);
- }
- break;
- }
- case "integer":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return 0L;
- }
- reader.Read();
- ret = Convert.ToInt64(reader.ReadString().Trim());
- break;
- }
- case "real":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return 0.0f;
- }
- reader.Read();
- ret = Convert.ToDouble(reader.ReadString().Trim());
- break;
- }
- case "uuid":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return new LLUUID();
- }
- reader.Read();
- ret = new LLUUID(reader.ReadString().Trim());
- break;
- }
- case "string":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return String.Empty;
- }
- reader.Read();
- ret = reader.ReadString();
- break;
- }
- case "binary":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read(); return new byte[0];
- }
- if (reader.GetAttribute("encoding") != null &&
- reader.GetAttribute("encoding") != "base64")
- throw new LLSDParseException("Unknown encoding: " +
- reader.GetAttribute("encoding"));
- reader.Read();
- FromBase64Transform b64 = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
- byte[] inp = Encoding.ASCII.GetBytes(reader.ReadString());
- ret = b64.TransformFinalBlock(inp, 0, inp.Length);
- break;
- }
- case "date":
- {
- reader.Read();
- throw new Exception("LLSD TODO: date");
- }
- case "map":
- {
- return LLSDParseMap(reader);
- }
- case "array":
- {
- return LLSDParseArray(reader);
- }
- default:
- throw new LLSDParseException("Unknown element <" + dtype + ">");
- }
- if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != dtype)
- {
- throw new LLSDParseException("Expected " + dtype + ">");
- }
- reader.Read();
- return ret;
- }
-
- ///
- ///
- ///
- ///
- ///
- public static Hashtable LLSDParseMap(XmlTextReader reader)
- {
- if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "map")
- throw new LLSDParseException("Expected