Initial commit

I wish I could figure out how to have PlexVie be out of the LibOMV tree but its just not working die to a DLL not found exception about libopenjpeg. (Yes the dll is in the bin folder)
This will do for now
This commit is contained in:
Blake Bourque
2015-06-18 22:15:24 -04:00
commit 0bb2dff347
382 changed files with 273704 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
compile.bat
*.csproj
*.user
*.userprefs
*.sln
*.suo
*.cache
[Oo]bj/
[Bb]in/
+24
View File
@@ -0,0 +1,24 @@
Copyright (c) 2006-2014, openmetaverse.org
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 openmetaverse.org 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.
@@ -0,0 +1,60 @@
#region Header
/*
* IJsonWrapper.cs
* Interface that represents a type capable of handling all kinds of JSON
* data. This is mainly used when mapping objects through JsonMapper, and
* it's implemented by JsonData.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System.Collections;
using System.Collections.Specialized;
namespace LitJson
{
public enum JsonType
{
None,
Object,
Array,
String,
Int,
Long,
Double,
Boolean
}
public interface IJsonWrapper : IList, IOrderedDictionary
{
bool IsArray { get; }
bool IsBoolean { get; }
bool IsDouble { get; }
bool IsInt { get; }
bool IsLong { get; }
bool IsObject { get; }
bool IsString { get; }
bool GetBoolean ();
double GetDouble ();
int GetInt ();
JsonType GetJsonType ();
long GetLong ();
string GetString ();
void SetBoolean (bool val);
void SetDouble (double val);
void SetInt (int val);
void SetJsonType (JsonType type);
void SetLong (long val);
void SetString (string val);
string ToJson ();
void ToJson (JsonWriter writer);
}
}
@@ -0,0 +1,993 @@
#region Header
/*
* JsonData.cs
* Generic type to hold JSON data (objects, arrays, and so on). This is
* the default type returned by JsonMapper.ToObject().
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
namespace LitJson
{
public class JsonData : IJsonWrapper, IEquatable<JsonData>
{
#region Fields
private IList<JsonData> inst_array;
private bool inst_boolean;
private double inst_double;
private int inst_int;
private long inst_long;
private IDictionary<string, JsonData> inst_object;
private string inst_string;
private string json;
private JsonType type;
// Used to implement the IOrderedDictionary interface
private IList<KeyValuePair<string, JsonData>> object_list;
#endregion
#region Properties
public int Count {
get { return EnsureCollection ().Count; }
}
public bool IsArray {
get { return type == JsonType.Array; }
}
public bool IsBoolean {
get { return type == JsonType.Boolean; }
}
public bool IsDouble {
get { return type == JsonType.Double; }
}
public bool IsInt {
get { return type == JsonType.Int; }
}
public bool IsLong {
get { return type == JsonType.Long; }
}
public bool IsObject {
get { return type == JsonType.Object; }
}
public bool IsString {
get { return type == JsonType.String; }
}
#endregion
#region ICollection Properties
int ICollection.Count {
get {
return Count;
}
}
bool ICollection.IsSynchronized {
get {
return EnsureCollection ().IsSynchronized;
}
}
object ICollection.SyncRoot {
get {
return EnsureCollection ().SyncRoot;
}
}
#endregion
#region IDictionary Properties
bool IDictionary.IsFixedSize {
get {
return EnsureDictionary ().IsFixedSize;
}
}
bool IDictionary.IsReadOnly {
get {
return EnsureDictionary ().IsReadOnly;
}
}
ICollection IDictionary.Keys {
get {
EnsureDictionary ();
IList<string> keys = new List<string> ();
foreach (KeyValuePair<string, JsonData> entry in
object_list) {
keys.Add (entry.Key);
}
return (ICollection) keys;
}
}
ICollection IDictionary.Values {
get {
EnsureDictionary ();
IList<JsonData> values = new List<JsonData> ();
foreach (KeyValuePair<string, JsonData> entry in
object_list) {
values.Add (entry.Value);
}
return (ICollection) values;
}
}
#endregion
#region IJsonWrapper Properties
bool IJsonWrapper.IsArray {
get { return IsArray; }
}
bool IJsonWrapper.IsBoolean {
get { return IsBoolean; }
}
bool IJsonWrapper.IsDouble {
get { return IsDouble; }
}
bool IJsonWrapper.IsInt {
get { return IsInt; }
}
bool IJsonWrapper.IsLong {
get { return IsLong; }
}
bool IJsonWrapper.IsObject {
get { return IsObject; }
}
bool IJsonWrapper.IsString {
get { return IsString; }
}
#endregion
#region IList Properties
bool IList.IsFixedSize {
get {
return EnsureList ().IsFixedSize;
}
}
bool IList.IsReadOnly {
get {
return EnsureList ().IsReadOnly;
}
}
#endregion
#region IDictionary Indexer
object IDictionary.this[object key] {
get {
return EnsureDictionary ()[key];
}
set {
if (! (key is String))
throw new ArgumentException (
"The key has to be a string");
JsonData data = ToJsonData (value);
this[(string) key] = data;
}
}
#endregion
#region IOrderedDictionary Indexer
object IOrderedDictionary.this[int idx] {
get {
EnsureDictionary ();
return object_list[idx].Value;
}
set {
EnsureDictionary ();
JsonData data = ToJsonData (value);
KeyValuePair<string, JsonData> old_entry = object_list[idx];
inst_object[old_entry.Key] = data;
KeyValuePair<string, JsonData> entry =
new KeyValuePair<string, JsonData> (old_entry.Key, data);
object_list[idx] = entry;
}
}
#endregion
#region IList Indexer
object IList.this[int index] {
get {
return EnsureList ()[index];
}
set {
EnsureList ();
JsonData data = ToJsonData (value);
this[index] = data;
}
}
#endregion
#region Public Indexers
public JsonData this[string prop_name] {
get {
EnsureDictionary ();
return inst_object[prop_name];
}
set {
EnsureDictionary ();
KeyValuePair<string, JsonData> entry =
new KeyValuePair<string, JsonData> (prop_name, value);
if (inst_object.ContainsKey (prop_name)) {
for (int i = 0; i < object_list.Count; i++) {
if (object_list[i].Key == prop_name) {
object_list[i] = entry;
break;
}
}
} else
object_list.Add (entry);
inst_object[prop_name] = value;
json = null;
}
}
public JsonData this[int index] {
get {
EnsureCollection ();
if (type == JsonType.Array)
return inst_array[index];
return object_list[index].Value;
}
set {
EnsureCollection ();
if (type == JsonType.Array)
inst_array[index] = value;
else {
KeyValuePair<string, JsonData> entry = object_list[index];
KeyValuePair<string, JsonData> new_entry =
new KeyValuePair<string, JsonData> (entry.Key, value);
object_list[index] = new_entry;
inst_object[entry.Key] = value;
}
json = null;
}
}
#endregion
#region Constructors
public JsonData ()
{
}
public JsonData (bool boolean)
{
type = JsonType.Boolean;
inst_boolean = boolean;
}
public JsonData (double number)
{
type = JsonType.Double;
inst_double = number;
}
public JsonData (int number)
{
type = JsonType.Int;
inst_int = number;
}
public JsonData (long number)
{
type = JsonType.Long;
inst_long = number;
}
public JsonData (object obj)
{
if (obj is Boolean) {
type = JsonType.Boolean;
inst_boolean = (bool) obj;
return;
}
if (obj is Double) {
type = JsonType.Double;
inst_double = (double) obj;
return;
}
if (obj is Int32) {
type = JsonType.Int;
inst_int = (int) obj;
return;
}
if (obj is Int64) {
type = JsonType.Long;
inst_long = (long) obj;
return;
}
if (obj is String) {
type = JsonType.String;
inst_string = (string) obj;
return;
}
throw new ArgumentException (
"Unable to wrap the given object with JsonData");
}
public JsonData (string str)
{
type = JsonType.String;
inst_string = str;
}
#endregion
#region Implicit Conversions
public static implicit operator JsonData (Boolean data)
{
return new JsonData (data);
}
public static implicit operator JsonData (Double data)
{
return new JsonData (data);
}
public static implicit operator JsonData (Int32 data)
{
return new JsonData (data);
}
public static implicit operator JsonData (Int64 data)
{
return new JsonData (data);
}
public static implicit operator JsonData (String data)
{
return new JsonData (data);
}
#endregion
#region Explicit Conversions
public static explicit operator Boolean (JsonData data)
{
if (data.type != JsonType.Boolean)
throw new InvalidCastException (
"Instance of JsonData doesn't hold a double");
return data.inst_boolean;
}
public static explicit operator Double (JsonData data)
{
if (data.type != JsonType.Double)
throw new InvalidCastException (
"Instance of JsonData doesn't hold a double");
return data.inst_double;
}
public static explicit operator Int32 (JsonData data)
{
if (data.type != JsonType.Int)
throw new InvalidCastException (
"Instance of JsonData doesn't hold an int");
return data.inst_int;
}
public static explicit operator Int64 (JsonData data)
{
if (data.type != JsonType.Long)
throw new InvalidCastException (
"Instance of JsonData doesn't hold an int");
return data.inst_long;
}
public static explicit operator String (JsonData data)
{
if (data.type != JsonType.String)
throw new InvalidCastException (
"Instance of JsonData doesn't hold a string");
return data.inst_string;
}
#endregion
#region ICollection Methods
void ICollection.CopyTo (Array array, int index)
{
EnsureCollection ().CopyTo (array, index);
}
#endregion
#region IDictionary Methods
void IDictionary.Add (object key, object value)
{
JsonData data = ToJsonData (value);
EnsureDictionary ().Add (key, data);
KeyValuePair<string, JsonData> entry =
new KeyValuePair<string, JsonData> ((string) key, data);
object_list.Add (entry);
json = null;
}
void IDictionary.Clear ()
{
EnsureDictionary ().Clear ();
object_list.Clear ();
json = null;
}
bool IDictionary.Contains (object key)
{
return EnsureDictionary ().Contains (key);
}
IDictionaryEnumerator IDictionary.GetEnumerator ()
{
return ((IOrderedDictionary) this).GetEnumerator ();
}
void IDictionary.Remove (object key)
{
EnsureDictionary ().Remove (key);
for (int i = 0; i < object_list.Count; i++) {
if (object_list[i].Key == (string) key) {
object_list.RemoveAt (i);
break;
}
}
json = null;
}
#endregion
#region IEnumerable Methods
IEnumerator IEnumerable.GetEnumerator ()
{
return EnsureCollection ().GetEnumerator ();
}
#endregion
#region IJsonWrapper Methods
bool IJsonWrapper.GetBoolean ()
{
if (type != JsonType.Boolean)
throw new InvalidOperationException (
"JsonData instance doesn't hold a boolean");
return inst_boolean;
}
double IJsonWrapper.GetDouble ()
{
if (type != JsonType.Double)
throw new InvalidOperationException (
"JsonData instance doesn't hold a double");
return inst_double;
}
int IJsonWrapper.GetInt ()
{
if (type != JsonType.Int)
throw new InvalidOperationException (
"JsonData instance doesn't hold an int");
return inst_int;
}
long IJsonWrapper.GetLong ()
{
if (type != JsonType.Long)
throw new InvalidOperationException (
"JsonData instance doesn't hold a long");
return inst_long;
}
string IJsonWrapper.GetString ()
{
if (type != JsonType.String)
throw new InvalidOperationException (
"JsonData instance doesn't hold a string");
return inst_string;
}
void IJsonWrapper.SetBoolean (bool val)
{
type = JsonType.Boolean;
inst_boolean = val;
json = null;
}
void IJsonWrapper.SetDouble (double val)
{
type = JsonType.Double;
inst_double = val;
json = null;
}
void IJsonWrapper.SetInt (int val)
{
type = JsonType.Int;
inst_int = val;
json = null;
}
void IJsonWrapper.SetLong (long val)
{
type = JsonType.Long;
inst_long = val;
json = null;
}
void IJsonWrapper.SetString (string val)
{
type = JsonType.String;
inst_string = val;
json = null;
}
string IJsonWrapper.ToJson ()
{
return ToJson ();
}
void IJsonWrapper.ToJson (JsonWriter writer)
{
ToJson (writer);
}
#endregion
#region IList Methods
int IList.Add (object value)
{
return Add (value);
}
void IList.Clear ()
{
EnsureList ().Clear ();
json = null;
}
bool IList.Contains (object value)
{
return EnsureList ().Contains (value);
}
int IList.IndexOf (object value)
{
return EnsureList ().IndexOf (value);
}
void IList.Insert (int index, object value)
{
EnsureList ().Insert (index, value);
json = null;
}
void IList.Remove (object value)
{
EnsureList ().Remove (value);
json = null;
}
void IList.RemoveAt (int index)
{
EnsureList ().RemoveAt (index);
json = null;
}
#endregion
#region IOrderedDictionary Methods
IDictionaryEnumerator IOrderedDictionary.GetEnumerator ()
{
EnsureDictionary ();
return new OrderedDictionaryEnumerator (
object_list.GetEnumerator ());
}
void IOrderedDictionary.Insert (int idx, object key, object value)
{
string property = (string) key;
JsonData data = ToJsonData (value);
this[property] = data;
KeyValuePair<string, JsonData> entry =
new KeyValuePair<string, JsonData> (property, data);
object_list.Insert (idx, entry);
}
void IOrderedDictionary.RemoveAt (int idx)
{
EnsureDictionary ();
inst_object.Remove (object_list[idx].Key);
object_list.RemoveAt (idx);
}
#endregion
#region Private Methods
private ICollection EnsureCollection ()
{
if (type == JsonType.Array)
return (ICollection) inst_array;
if (type == JsonType.Object)
return (ICollection) inst_object;
throw new InvalidOperationException (
"The JsonData instance has to be initialized first");
}
private IDictionary EnsureDictionary ()
{
if (type == JsonType.Object)
return (IDictionary) inst_object;
if (type != JsonType.None)
throw new InvalidOperationException (
"Instance of JsonData is not a dictionary");
type = JsonType.Object;
inst_object = new Dictionary<string, JsonData> ();
object_list = new List<KeyValuePair<string, JsonData>> ();
return (IDictionary) inst_object;
}
private IList EnsureList ()
{
if (type == JsonType.Array)
return (IList) inst_array;
if (type != JsonType.None)
throw new InvalidOperationException (
"Instance of JsonData is not a list");
type = JsonType.Array;
inst_array = new List<JsonData> ();
return (IList) inst_array;
}
private JsonData ToJsonData (object obj)
{
if (obj == null)
return null;
if (obj is JsonData)
return (JsonData) obj;
return new JsonData (obj);
}
private static void WriteJson (IJsonWrapper obj, JsonWriter writer)
{
if (obj.IsString) {
writer.Write (obj.GetString ());
return;
}
if (obj.IsBoolean) {
writer.Write (obj.GetBoolean ());
return;
}
if (obj.IsDouble) {
writer.Write (obj.GetDouble ());
return;
}
if (obj.IsInt) {
writer.Write (obj.GetInt ());
return;
}
if (obj.IsLong) {
writer.Write (obj.GetLong ());
return;
}
if (obj.IsArray) {
writer.WriteArrayStart ();
foreach (object elem in (IList) obj)
WriteJson ((JsonData) elem, writer);
writer.WriteArrayEnd ();
return;
}
if (obj.IsObject) {
writer.WriteObjectStart ();
foreach (DictionaryEntry entry in ((IDictionary) obj)) {
writer.WritePropertyName ((string) entry.Key);
WriteJson ((JsonData) entry.Value, writer);
}
writer.WriteObjectEnd ();
return;
}
}
#endregion
public int Add (object value)
{
JsonData data = ToJsonData (value);
json = null;
return EnsureList ().Add (data);
}
public void Clear ()
{
if (IsObject) {
((IDictionary) this).Clear ();
return;
}
if (IsArray) {
((IList) this).Clear ();
return;
}
}
public bool Equals (JsonData x)
{
if (x == null)
return false;
if (x.type != this.type)
return false;
switch (this.type) {
case JsonType.None:
return true;
case JsonType.Object:
return this.inst_object.Equals (x.inst_object);
case JsonType.Array:
return this.inst_array.Equals (x.inst_array);
case JsonType.String:
return this.inst_string.Equals (x.inst_string);
case JsonType.Int:
return this.inst_int.Equals (x.inst_int);
case JsonType.Long:
return this.inst_long.Equals (x.inst_long);
case JsonType.Double:
return this.inst_double.Equals (x.inst_double);
case JsonType.Boolean:
return this.inst_boolean.Equals (x.inst_boolean);
}
return false;
}
public JsonType GetJsonType ()
{
return type;
}
public void SetJsonType (JsonType type)
{
if (this.type == type)
return;
switch (type) {
case JsonType.None:
break;
case JsonType.Object:
inst_object = new Dictionary<string, JsonData> ();
object_list = new List<KeyValuePair<string, JsonData>> ();
break;
case JsonType.Array:
inst_array = new List<JsonData> ();
break;
case JsonType.String:
inst_string = default (String);
break;
case JsonType.Int:
inst_int = default (Int32);
break;
case JsonType.Long:
inst_long = default (Int64);
break;
case JsonType.Double:
inst_double = default (Double);
break;
case JsonType.Boolean:
inst_boolean = default (Boolean);
break;
}
this.type = type;
}
public string ToJson ()
{
if (json != null)
return json;
StringWriter sw = new StringWriter ();
JsonWriter writer = new JsonWriter (sw);
writer.Validate = false;
WriteJson (this, writer);
json = sw.ToString ();
return json;
}
public void ToJson (JsonWriter writer)
{
bool old_validate = writer.Validate;
writer.Validate = false;
WriteJson (this, writer);
writer.Validate = old_validate;
}
public override string ToString ()
{
switch (type) {
case JsonType.Array:
return "JsonData array";
case JsonType.Boolean:
return inst_boolean.ToString ();
case JsonType.Double:
return inst_double.ToString ();
case JsonType.Int:
return inst_int.ToString ();
case JsonType.Long:
return inst_long.ToString ();
case JsonType.Object:
return "JsonData object";
case JsonType.String:
return inst_string;
}
return "Uninitialized JsonData";
}
}
internal class OrderedDictionaryEnumerator : IDictionaryEnumerator
{
IEnumerator<KeyValuePair<string, JsonData>> list_enumerator;
public object Current {
get { return Entry; }
}
public DictionaryEntry Entry {
get {
KeyValuePair<string, JsonData> curr = list_enumerator.Current;
return new DictionaryEntry (curr.Key, curr.Value);
}
}
public object Key {
get { return list_enumerator.Current.Key; }
}
public object Value {
get { return list_enumerator.Current.Value; }
}
public OrderedDictionaryEnumerator (
IEnumerator<KeyValuePair<string, JsonData>> enumerator)
{
list_enumerator = enumerator;
}
public bool MoveNext ()
{
return list_enumerator.MoveNext ();
}
public void Reset ()
{
list_enumerator.Reset ();
}
}
}
@@ -0,0 +1,60 @@
#region Header
/*
* JsonException.cs
* Base class throwed by LitJSON when a parsing error occurs.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System;
namespace LitJson
{
public class JsonException : ApplicationException
{
public JsonException () : base ()
{
}
internal JsonException (ParserToken token) :
base (String.Format (
"Invalid token '{0}' in input string", token))
{
}
internal JsonException (ParserToken token,
Exception inner_exception) :
base (String.Format (
"Invalid token '{0}' in input string", token),
inner_exception)
{
}
internal JsonException (int c) :
base (String.Format (
"Invalid character '{0}' in input string", (char) c))
{
}
internal JsonException (int c, Exception inner_exception) :
base (String.Format (
"Invalid character '{0}' in input string", (char) c),
inner_exception)
{
}
public JsonException (string message) : base (message)
{
}
public JsonException (string message, Exception inner_exception) :
base (message, inner_exception)
{
}
}
}
@@ -0,0 +1,901 @@
#region Header
/*
* JsonMapper.cs
* JSON to .Net object and object to JSON conversions.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
namespace LitJson
{
internal struct PropertyMetadata
{
public MemberInfo Info;
public bool IsField;
public Type Type;
}
internal struct ArrayMetadata
{
private Type element_type;
private bool is_array;
private bool is_list;
public Type ElementType {
get {
if (element_type == null)
return typeof (JsonData);
return element_type;
}
set { element_type = value; }
}
public bool IsArray {
get { return is_array; }
set { is_array = value; }
}
public bool IsList {
get { return is_list; }
set { is_list = value; }
}
}
internal struct ObjectMetadata
{
private Type element_type;
private bool is_dictionary;
private IDictionary<string, PropertyMetadata> properties;
public Type ElementType {
get {
if (element_type == null)
return typeof (JsonData);
return element_type;
}
set { element_type = value; }
}
public bool IsDictionary {
get { return is_dictionary; }
set { is_dictionary = value; }
}
public IDictionary<string, PropertyMetadata> Properties {
get { return properties; }
set { properties = value; }
}
}
internal delegate void ExporterFunc (object obj, JsonWriter writer);
public delegate void ExporterFunc<T> (T obj, JsonWriter writer);
internal delegate object ImporterFunc (object input);
public delegate TValue ImporterFunc<TJson, TValue> (TJson input);
public delegate IJsonWrapper WrapperFactory ();
public class JsonMapper
{
#region Fields
private static int max_nesting_depth;
private static IFormatProvider datetime_format;
private static IDictionary<Type, ExporterFunc> base_exporters_table;
private static IDictionary<Type, ExporterFunc> custom_exporters_table;
private static IDictionary<Type,
IDictionary<Type, ImporterFunc>> base_importers_table;
private static IDictionary<Type,
IDictionary<Type, ImporterFunc>> custom_importers_table;
private static IDictionary<Type, ArrayMetadata> array_metadata;
private static readonly object array_metadata_lock = new Object ();
private static IDictionary<Type,
IDictionary<Type, MethodInfo>> conv_ops;
private static readonly object conv_ops_lock = new Object ();
private static IDictionary<Type, ObjectMetadata> object_metadata;
private static readonly object object_metadata_lock = new Object ();
private static IDictionary<Type,
IList<PropertyMetadata>> type_properties;
private static readonly object type_properties_lock = new Object ();
private static JsonWriter static_writer;
private static readonly object static_writer_lock = new Object ();
#endregion
#region Constructors
static JsonMapper ()
{
max_nesting_depth = 100;
array_metadata = new Dictionary<Type, ArrayMetadata> ();
conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>> ();
object_metadata = new Dictionary<Type, ObjectMetadata> ();
type_properties = new Dictionary<Type,
IList<PropertyMetadata>> ();
static_writer = new JsonWriter ();
datetime_format = DateTimeFormatInfo.InvariantInfo;
base_exporters_table = new Dictionary<Type, ExporterFunc> ();
custom_exporters_table = new Dictionary<Type, ExporterFunc> ();
base_importers_table = new Dictionary<Type,
IDictionary<Type, ImporterFunc>> ();
custom_importers_table = new Dictionary<Type,
IDictionary<Type, ImporterFunc>> ();
RegisterBaseExporters ();
RegisterBaseImporters ();
}
#endregion
#region Private Methods
private static void AddArrayMetadata (Type type)
{
if (array_metadata.ContainsKey (type))
return;
ArrayMetadata data = new ArrayMetadata ();
data.IsArray = type.IsArray;
if (type.GetInterface ("System.Collections.IList") != null)
data.IsList = true;
foreach (PropertyInfo p_info in type.GetProperties ()) {
if (p_info.Name != "Item")
continue;
ParameterInfo[] parameters = p_info.GetIndexParameters ();
if (parameters.Length != 1)
continue;
if (parameters[0].ParameterType == typeof (int))
data.ElementType = p_info.PropertyType;
}
lock (array_metadata_lock) {
try {
array_metadata.Add (type, data);
} catch (ArgumentException) {
return;
}
}
}
private static void AddObjectMetadata (Type type)
{
if (object_metadata.ContainsKey (type))
return;
ObjectMetadata data = new ObjectMetadata ();
if (type.GetInterface ("System.Collections.IDictionary") != null)
data.IsDictionary = true;
data.Properties = new Dictionary<string, PropertyMetadata> ();
foreach (PropertyInfo p_info in type.GetProperties ()) {
if (p_info.Name == "Item") {
ParameterInfo[] parameters = p_info.GetIndexParameters ();
if (parameters.Length != 1)
continue;
if (parameters[0].ParameterType == typeof (string))
data.ElementType = p_info.PropertyType;
continue;
}
PropertyMetadata p_data = new PropertyMetadata ();
p_data.Info = p_info;
p_data.Type = p_info.PropertyType;
data.Properties.Add (p_info.Name, p_data);
}
foreach (FieldInfo f_info in type.GetFields ()) {
PropertyMetadata p_data = new PropertyMetadata ();
p_data.Info = f_info;
p_data.IsField = true;
p_data.Type = f_info.FieldType;
data.Properties.Add (f_info.Name, p_data);
}
lock (object_metadata_lock) {
try {
object_metadata.Add (type, data);
} catch (ArgumentException) {
return;
}
}
}
private static void AddTypeProperties (Type type)
{
if (type_properties.ContainsKey (type))
return;
IList<PropertyMetadata> props = new List<PropertyMetadata> ();
foreach (PropertyInfo p_info in type.GetProperties ()) {
if (p_info.Name == "Item")
continue;
PropertyMetadata p_data = new PropertyMetadata ();
p_data.Info = p_info;
p_data.IsField = false;
props.Add (p_data);
}
foreach (FieldInfo f_info in type.GetFields ()) {
PropertyMetadata p_data = new PropertyMetadata ();
p_data.Info = f_info;
p_data.IsField = true;
props.Add (p_data);
}
lock (type_properties_lock) {
try {
type_properties.Add (type, props);
} catch (ArgumentException) {
return;
}
}
}
private static MethodInfo GetConvOp (Type t1, Type t2)
{
lock (conv_ops_lock) {
if (! conv_ops.ContainsKey (t1))
conv_ops.Add (t1, new Dictionary<Type, MethodInfo> ());
}
if (conv_ops[t1].ContainsKey (t2))
return conv_ops[t1][t2];
MethodInfo op = t1.GetMethod (
"op_Implicit", new Type[] { t2 });
lock (conv_ops_lock) {
try {
conv_ops[t1].Add (t2, op);
} catch (ArgumentException) {
return conv_ops[t1][t2];
}
}
return op;
}
private static object ReadValue (Type inst_type, JsonReader reader)
{
reader.Read ();
if (reader.Token == JsonToken.ArrayEnd)
return null;
if (reader.Token == JsonToken.Null) {
if (! inst_type.IsClass)
throw new JsonException (String.Format (
"Can't assign null to an instance of type {0}",
inst_type));
return null;
}
if (reader.Token == JsonToken.Double ||
reader.Token == JsonToken.Int ||
reader.Token == JsonToken.Long ||
reader.Token == JsonToken.String ||
reader.Token == JsonToken.Boolean) {
Type json_type = reader.Value.GetType ();
if (inst_type.IsAssignableFrom (json_type))
return reader.Value;
// If there's a custom importer that fits, use it
if (custom_importers_table.ContainsKey (json_type) &&
custom_importers_table[json_type].ContainsKey (
inst_type)) {
ImporterFunc importer =
custom_importers_table[json_type][inst_type];
return importer (reader.Value);
}
// Maybe there's a base importer that works
if (base_importers_table.ContainsKey (json_type) &&
base_importers_table[json_type].ContainsKey (
inst_type)) {
ImporterFunc importer =
base_importers_table[json_type][inst_type];
return importer (reader.Value);
}
// Maybe it's an enum
if (inst_type.IsEnum)
return Enum.ToObject (inst_type, reader.Value);
// Try using an implicit conversion operator
MethodInfo conv_op = GetConvOp (inst_type, json_type);
if (conv_op != null)
return conv_op.Invoke (null,
new object[] { reader.Value });
// No luck
throw new JsonException (String.Format (
"Can't assign value '{0}' (type {1}) to type {2}",
reader.Value, json_type, inst_type));
}
object instance = null;
if (reader.Token == JsonToken.ArrayStart) {
AddArrayMetadata (inst_type);
ArrayMetadata t_data = array_metadata[inst_type];
if (! t_data.IsArray && ! t_data.IsList)
throw new JsonException (String.Format (
"Type {0} can't act as an array",
inst_type));
IList list;
Type elem_type;
if (! t_data.IsArray) {
list = (IList) Activator.CreateInstance (inst_type);
elem_type = t_data.ElementType;
} else {
list = new ArrayList ();
elem_type = inst_type.GetElementType ();
}
while (true) {
object item = ReadValue (elem_type, reader);
if (reader.Token == JsonToken.ArrayEnd)
break;
list.Add (item);
}
if (t_data.IsArray) {
int n = list.Count;
instance = Array.CreateInstance (elem_type, n);
for (int i = 0; i < n; i++)
((Array) instance).SetValue (list[i], i);
} else
instance = list;
} else if (reader.Token == JsonToken.ObjectStart) {
AddObjectMetadata (inst_type);
ObjectMetadata t_data = object_metadata[inst_type];
instance = Activator.CreateInstance (inst_type);
while (true) {
reader.Read ();
if (reader.Token == JsonToken.ObjectEnd)
break;
string property = (string) reader.Value;
if (t_data.Properties.ContainsKey (property)) {
PropertyMetadata prop_data =
t_data.Properties[property];
if (prop_data.IsField) {
((FieldInfo) prop_data.Info).SetValue (
instance, ReadValue (prop_data.Type, reader));
} else {
PropertyInfo p_info =
(PropertyInfo) prop_data.Info;
if (p_info.CanWrite)
p_info.SetValue (
instance,
ReadValue (prop_data.Type, reader),
null);
else
ReadValue (prop_data.Type, reader);
}
} else {
if (! t_data.IsDictionary)
throw new JsonException (String.Format (
"The type {0} doesn't have the " +
"property '{1}'", inst_type, property));
((IDictionary) instance).Add (
property, ReadValue (
t_data.ElementType, reader));
}
}
}
return instance;
}
private static IJsonWrapper ReadValue (WrapperFactory factory,
JsonReader reader)
{
reader.Read ();
if (reader.Token == JsonToken.ArrayEnd ||
reader.Token == JsonToken.Null)
return null;
IJsonWrapper instance = factory ();
switch (reader.Token)
{
case JsonToken.String:
instance.SetString ((string) reader.Value);
break;
case JsonToken.Double:
instance.SetDouble ((double) reader.Value);
break;
case JsonToken.Int:
instance.SetInt ((int) reader.Value);
break;
case JsonToken.Long:
instance.SetLong ((long) reader.Value);
break;
case JsonToken.Boolean:
instance.SetBoolean ((bool) reader.Value);
break;
case JsonToken.ArrayStart:
instance.SetJsonType (JsonType.Array);
while (true) {
IJsonWrapper item = ReadValue (factory, reader);
if (item == null && reader.Token == JsonToken.ArrayEnd)
break;
((IList) instance).Add (item);
}
break;
case JsonToken.ObjectStart:
instance.SetJsonType (JsonType.Object);
while (true) {
reader.Read ();
if (reader.Token == JsonToken.ObjectEnd)
break;
string property = (string) reader.Value;
((IDictionary) instance)[property] = ReadValue (
factory, reader);
}
break;
}
return instance;
}
private static void RegisterBaseExporters ()
{
base_exporters_table[typeof (byte)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToInt32 ((byte) obj));
};
base_exporters_table[typeof (char)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToString ((char) obj));
};
base_exporters_table[typeof (DateTime)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToString ((DateTime) obj,
datetime_format));
};
base_exporters_table[typeof (decimal)] =
delegate (object obj, JsonWriter writer) {
writer.Write ((decimal) obj);
};
base_exporters_table[typeof (sbyte)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToInt32 ((sbyte) obj));
};
base_exporters_table[typeof (short)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToInt32 ((short) obj));
};
base_exporters_table[typeof (ushort)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToInt32 ((ushort) obj));
};
base_exporters_table[typeof (uint)] =
delegate (object obj, JsonWriter writer) {
writer.Write (Convert.ToUInt64 ((uint) obj));
};
base_exporters_table[typeof (ulong)] =
delegate (object obj, JsonWriter writer) {
writer.Write ((ulong) obj);
};
}
private static void RegisterBaseImporters ()
{
ImporterFunc importer;
importer = delegate (object input) {
return Convert.ToByte ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (byte), importer);
importer = delegate (object input) {
return Convert.ToUInt64 ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (ulong), importer);
importer = delegate (object input) {
return Convert.ToSByte ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (sbyte), importer);
importer = delegate (object input) {
return Convert.ToInt16 ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (short), importer);
importer = delegate (object input) {
return Convert.ToUInt16 ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (ushort), importer);
importer = delegate (object input) {
return Convert.ToUInt32 ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (uint), importer);
importer = delegate (object input) {
return Convert.ToSingle ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (float), importer);
importer = delegate (object input) {
return Convert.ToDouble ((int) input);
};
RegisterImporter (base_importers_table, typeof (int),
typeof (double), importer);
importer = delegate (object input) {
return Convert.ToDecimal ((double) input);
};
RegisterImporter (base_importers_table, typeof (double),
typeof (decimal), importer);
importer = delegate (object input) {
return Convert.ToUInt32 ((long) input);
};
RegisterImporter (base_importers_table, typeof (long),
typeof (uint), importer);
importer = delegate (object input) {
return Convert.ToChar ((string) input);
};
RegisterImporter (base_importers_table, typeof (string),
typeof (char), importer);
importer = delegate (object input) {
return Convert.ToDateTime ((string) input, datetime_format);
};
RegisterImporter (base_importers_table, typeof (string),
typeof (DateTime), importer);
}
private static void RegisterImporter (
IDictionary<Type, IDictionary<Type, ImporterFunc>> table,
Type json_type, Type value_type, ImporterFunc importer)
{
if (! table.ContainsKey (json_type))
table.Add (json_type, new Dictionary<Type, ImporterFunc> ());
table[json_type][value_type] = importer;
}
private static void WriteValue (object obj, JsonWriter writer,
bool writer_is_private,
int depth)
{
if (depth > max_nesting_depth)
throw new JsonException (
String.Format ("Max allowed object depth reached while " +
"trying to export from type {0}",
obj.GetType ()));
if (obj == null) {
writer.Write (null);
return;
}
if (obj is IJsonWrapper) {
if (writer_is_private)
writer.TextWriter.Write (((IJsonWrapper) obj).ToJson ());
else
((IJsonWrapper) obj).ToJson (writer);
return;
}
if (obj is String) {
writer.Write ((string) obj);
return;
}
if (obj is Double) {
writer.Write ((double) obj);
return;
}
if (obj is Int32) {
writer.Write ((int) obj);
return;
}
if (obj is Boolean) {
writer.Write ((bool) obj);
return;
}
if (obj is Int64) {
writer.Write ((long) obj);
return;
}
if (obj is Array) {
writer.WriteArrayStart ();
foreach (object elem in (Array) obj)
WriteValue (elem, writer, writer_is_private, depth + 1);
writer.WriteArrayEnd ();
return;
}
if (obj is IList) {
writer.WriteArrayStart ();
foreach (object elem in (IList) obj)
WriteValue (elem, writer, writer_is_private, depth + 1);
writer.WriteArrayEnd ();
return;
}
if (obj is IDictionary) {
writer.WriteObjectStart ();
foreach (DictionaryEntry entry in (IDictionary) obj) {
writer.WritePropertyName ((string) entry.Key);
WriteValue (entry.Value, writer, writer_is_private,
depth + 1);
}
writer.WriteObjectEnd ();
return;
}
Type obj_type = obj.GetType ();
// See if there's a custom exporter for the object
if (custom_exporters_table.ContainsKey (obj_type)) {
ExporterFunc exporter = custom_exporters_table[obj_type];
exporter (obj, writer);
return;
}
// If not, maybe there's a base exporter
if (base_exporters_table.ContainsKey (obj_type)) {
ExporterFunc exporter = base_exporters_table[obj_type];
exporter (obj, writer);
return;
}
// Last option, let's see if it's an enum
if (obj is Enum) {
Type e_type = Enum.GetUnderlyingType (obj_type);
if (e_type == typeof (long)
|| e_type == typeof (uint)
|| e_type == typeof (ulong))
writer.Write ((ulong) obj);
else
writer.Write ((int) obj);
return;
}
// Okay, so it looks like the input should be exported as an
// object
AddTypeProperties (obj_type);
IList<PropertyMetadata> props = type_properties[obj_type];
writer.WriteObjectStart ();
foreach (PropertyMetadata p_data in props) {
if (p_data.IsField) {
writer.WritePropertyName (p_data.Info.Name);
WriteValue (((FieldInfo) p_data.Info).GetValue (obj),
writer, writer_is_private, depth + 1);
}
else {
PropertyInfo p_info = (PropertyInfo) p_data.Info;
if (p_info.CanRead) {
writer.WritePropertyName (p_data.Info.Name);
WriteValue (p_info.GetValue (obj, null),
writer, writer_is_private, depth + 1);
}
}
}
writer.WriteObjectEnd ();
}
#endregion
public static string ToJson (object obj)
{
lock (static_writer_lock) {
static_writer.Reset ();
WriteValue (obj, static_writer, true, 0);
return static_writer.ToString ();
}
}
public static void ToJson (object obj, JsonWriter writer)
{
WriteValue (obj, writer, false, 0);
}
public static JsonData ToObject (JsonReader reader)
{
return (JsonData) ToWrapper (
delegate { return new JsonData (); }, reader);
}
public static JsonData ToObject (TextReader reader)
{
JsonReader json_reader = new JsonReader (reader);
return (JsonData) ToWrapper (
delegate { return new JsonData (); }, json_reader);
}
public static JsonData ToObject (string json)
{
return (JsonData) ToWrapper (
delegate { return new JsonData (); }, json);
}
public static T ToObject<T> (JsonReader reader)
{
return (T) ReadValue (typeof (T), reader);
}
public static T ToObject<T> (TextReader reader)
{
JsonReader json_reader = new JsonReader (reader);
return (T) ReadValue (typeof (T), json_reader);
}
public static T ToObject<T> (string json)
{
JsonReader reader = new JsonReader (json);
return (T) ReadValue (typeof (T), reader);
}
public static IJsonWrapper ToWrapper (WrapperFactory factory,
JsonReader reader)
{
return ReadValue (factory, reader);
}
public static IJsonWrapper ToWrapper (WrapperFactory factory,
string json)
{
JsonReader reader = new JsonReader (json);
return ReadValue (factory, reader);
}
public static void RegisterExporter<T> (ExporterFunc<T> exporter)
{
ExporterFunc exporter_wrapper =
delegate (object obj, JsonWriter writer) {
exporter ((T) obj, writer);
};
custom_exporters_table[typeof (T)] = exporter_wrapper;
}
public static void RegisterImporter<TJson, TValue> (
ImporterFunc<TJson, TValue> importer)
{
ImporterFunc importer_wrapper =
delegate (object input) {
return importer ((TJson) input);
};
RegisterImporter (custom_importers_table, typeof (TJson),
typeof (TValue), importer_wrapper);
}
public static void UnregisterExporters ()
{
custom_exporters_table.Clear ();
}
public static void UnregisterImporters ()
{
custom_importers_table.Clear ();
}
}
}
@@ -0,0 +1,455 @@
#region Header
/*
* JsonReader.cs
* Stream-like access to JSON text.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace LitJson
{
public enum JsonToken
{
None,
ObjectStart,
PropertyName,
ObjectEnd,
ArrayStart,
ArrayEnd,
Int,
Long,
Double,
String,
Boolean,
Null
}
public class JsonReader
{
#region Fields
private static IDictionary<int, IDictionary<int, int[]>> parse_table;
private Stack<int> automaton_stack;
private int current_input;
private int current_symbol;
private bool end_of_json;
private bool end_of_input;
private Lexer lexer;
private bool parser_in_string;
private bool parser_return;
private bool read_started;
private TextReader reader;
private bool reader_is_owned;
private object token_value;
private JsonToken token;
#endregion
#region Public Properties
public bool AllowComments {
get { return lexer.AllowComments; }
set { lexer.AllowComments = value; }
}
public bool AllowSingleQuotedStrings {
get { return lexer.AllowSingleQuotedStrings; }
set { lexer.AllowSingleQuotedStrings = value; }
}
public bool EndOfInput {
get { return end_of_input; }
}
public bool EndOfJson {
get { return end_of_json; }
}
public JsonToken Token {
get { return token; }
}
public object Value {
get { return token_value; }
}
#endregion
#region Constructors
static JsonReader ()
{
PopulateParseTable ();
}
public JsonReader (string json_text) :
this (new StringReader (json_text), true)
{
}
public JsonReader (TextReader reader) :
this (reader, false)
{
}
private JsonReader (TextReader reader, bool owned)
{
if (reader == null)
throw new ArgumentNullException ("reader");
parser_in_string = false;
parser_return = false;
read_started = false;
automaton_stack = new Stack<int> ();
automaton_stack.Push ((int) ParserToken.End);
automaton_stack.Push ((int) ParserToken.Text);
lexer = new Lexer (reader);
end_of_input = false;
end_of_json = false;
this.reader = reader;
reader_is_owned = owned;
}
#endregion
#region Static Methods
private static void PopulateParseTable ()
{
parse_table = new Dictionary<int, IDictionary<int, int[]>> ();
TableAddRow (ParserToken.Array);
TableAddCol (ParserToken.Array, '[',
'[',
(int) ParserToken.ArrayPrime);
TableAddRow (ParserToken.ArrayPrime);
TableAddCol (ParserToken.ArrayPrime, '"',
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddCol (ParserToken.ArrayPrime, '[',
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddCol (ParserToken.ArrayPrime, ']',
']');
TableAddCol (ParserToken.ArrayPrime, '{',
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Number,
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.True,
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.False,
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Null,
(int) ParserToken.Value,
(int) ParserToken.ValueRest,
']');
TableAddRow (ParserToken.Object);
TableAddCol (ParserToken.Object, '{',
'{',
(int) ParserToken.ObjectPrime);
TableAddRow (ParserToken.ObjectPrime);
TableAddCol (ParserToken.ObjectPrime, '"',
(int) ParserToken.Pair,
(int) ParserToken.PairRest,
'}');
TableAddCol (ParserToken.ObjectPrime, '}',
'}');
TableAddRow (ParserToken.Pair);
TableAddCol (ParserToken.Pair, '"',
(int) ParserToken.String,
':',
(int) ParserToken.Value);
TableAddRow (ParserToken.PairRest);
TableAddCol (ParserToken.PairRest, ',',
',',
(int) ParserToken.Pair,
(int) ParserToken.PairRest);
TableAddCol (ParserToken.PairRest, '}',
(int) ParserToken.Epsilon);
TableAddRow (ParserToken.String);
TableAddCol (ParserToken.String, '"',
'"',
(int) ParserToken.CharSeq,
'"');
TableAddRow (ParserToken.Text);
TableAddCol (ParserToken.Text, '[',
(int) ParserToken.Array);
TableAddCol (ParserToken.Text, '{',
(int) ParserToken.Object);
TableAddRow (ParserToken.Value);
TableAddCol (ParserToken.Value, '"',
(int) ParserToken.String);
TableAddCol (ParserToken.Value, '[',
(int) ParserToken.Array);
TableAddCol (ParserToken.Value, '{',
(int) ParserToken.Object);
TableAddCol (ParserToken.Value, (int) ParserToken.Number,
(int) ParserToken.Number);
TableAddCol (ParserToken.Value, (int) ParserToken.True,
(int) ParserToken.True);
TableAddCol (ParserToken.Value, (int) ParserToken.False,
(int) ParserToken.False);
TableAddCol (ParserToken.Value, (int) ParserToken.Null,
(int) ParserToken.Null);
TableAddRow (ParserToken.ValueRest);
TableAddCol (ParserToken.ValueRest, ',',
',',
(int) ParserToken.Value,
(int) ParserToken.ValueRest);
TableAddCol (ParserToken.ValueRest, ']',
(int) ParserToken.Epsilon);
}
private static void TableAddCol (ParserToken row, int col,
params int[] symbols)
{
parse_table[(int) row].Add (col, symbols);
}
private static void TableAddRow (ParserToken rule)
{
parse_table.Add ((int) rule, new Dictionary<int, int[]> ());
}
#endregion
#region Private Methods
private void ProcessNumber (string number)
{
if (number.IndexOf ('.') != -1 ||
number.IndexOf ('e') != -1 ||
number.IndexOf ('E') != -1) {
double n_double;
if (Double.TryParse (number, out n_double)) {
token = JsonToken.Double;
token_value = n_double;
return;
}
}
int n_int32;
if (Int32.TryParse (number, out n_int32)) {
token = JsonToken.Int;
token_value = n_int32;
return;
}
long n_int64;
if (Int64.TryParse (number, out n_int64)) {
token = JsonToken.Long;
token_value = n_int64;
return;
}
// Shouldn't happen, but just in case, return something
token = JsonToken.Int;
token_value = 0;
}
private void ProcessSymbol ()
{
if (current_symbol == '[') {
token = JsonToken.ArrayStart;
parser_return = true;
} else if (current_symbol == ']') {
token = JsonToken.ArrayEnd;
parser_return = true;
} else if (current_symbol == '{') {
token = JsonToken.ObjectStart;
parser_return = true;
} else if (current_symbol == '}') {
token = JsonToken.ObjectEnd;
parser_return = true;
} else if (current_symbol == '"') {
if (parser_in_string) {
parser_in_string = false;
parser_return = true;
} else {
if (token == JsonToken.None)
token = JsonToken.String;
parser_in_string = true;
}
} else if (current_symbol == (int) ParserToken.CharSeq) {
token_value = lexer.StringValue;
} else if (current_symbol == (int) ParserToken.False) {
token = JsonToken.Boolean;
token_value = false;
parser_return = true;
} else if (current_symbol == (int) ParserToken.Null) {
token = JsonToken.Null;
parser_return = true;
} else if (current_symbol == (int) ParserToken.Number) {
ProcessNumber (lexer.StringValue);
parser_return = true;
} else if (current_symbol == (int) ParserToken.Pair) {
token = JsonToken.PropertyName;
} else if (current_symbol == (int) ParserToken.True) {
token = JsonToken.Boolean;
token_value = true;
parser_return = true;
}
}
private bool ReadToken ()
{
if (end_of_input)
return false;
lexer.NextToken ();
if (lexer.EndOfInput) {
Close ();
return false;
}
current_input = lexer.Token;
return true;
}
#endregion
public void Close ()
{
if (end_of_input)
return;
end_of_input = true;
end_of_json = true;
if (reader_is_owned)
reader.Close ();
reader = null;
}
public bool Read ()
{
if (end_of_input)
return false;
if (end_of_json) {
end_of_json = false;
automaton_stack.Clear ();
automaton_stack.Push ((int) ParserToken.End);
automaton_stack.Push ((int) ParserToken.Text);
}
parser_in_string = false;
parser_return = false;
token = JsonToken.None;
token_value = null;
if (! read_started) {
read_started = true;
if (! ReadToken ())
return false;
}
int[] entry_symbols;
while (true) {
if (parser_return) {
if (automaton_stack.Peek () == (int) ParserToken.End)
end_of_json = true;
return true;
}
current_symbol = automaton_stack.Pop ();
ProcessSymbol ();
if (current_symbol == current_input) {
if (! ReadToken ()) {
if (automaton_stack.Peek () != (int) ParserToken.End)
throw new JsonException (
"Input doesn't evaluate to proper JSON text");
if (parser_return)
return true;
return false;
}
continue;
}
try {
entry_symbols =
parse_table[current_symbol][current_input];
} catch (KeyNotFoundException e) {
throw new JsonException ((ParserToken) current_input, e);
}
if (entry_symbols[0] == (int) ParserToken.Epsilon)
continue;
for (int i = entry_symbols.Length - 1; i >= 0; i--)
automaton_stack.Push (entry_symbols[i]);
}
}
}
}
@@ -0,0 +1,466 @@
#region Header
/*
* JsonWriter.cs
* Stream-like facility to output JSON text.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace LitJson
{
internal enum Condition
{
InArray,
InObject,
NotAProperty,
Property,
Value
}
internal class WriterContext
{
public int Count;
public bool InArray;
public bool InObject;
public bool ExpectingValue;
public int Padding;
}
public class JsonWriter
{
#region Fields
private static NumberFormatInfo number_format;
private WriterContext context;
private Stack<WriterContext> ctx_stack;
private bool has_reached_end;
private char[] hex_seq;
private int indentation;
private int indent_value;
private StringBuilder inst_string_builder;
private bool pretty_print;
private bool validate;
private TextWriter writer;
#endregion
#region Properties
public int IndentValue {
get { return indent_value; }
set {
indentation = (indentation / indent_value) * value;
indent_value = value;
}
}
public bool PrettyPrint {
get { return pretty_print; }
set { pretty_print = value; }
}
public TextWriter TextWriter {
get { return writer; }
}
public bool Validate {
get { return validate; }
set { validate = value; }
}
#endregion
#region Constructors
static JsonWriter ()
{
number_format = NumberFormatInfo.InvariantInfo;
}
public JsonWriter ()
{
inst_string_builder = new StringBuilder ();
writer = new StringWriter (inst_string_builder);
Init ();
}
public JsonWriter (StringBuilder sb) :
this (new StringWriter (sb))
{
}
public JsonWriter (TextWriter writer)
{
if (writer == null)
throw new ArgumentNullException ("writer");
this.writer = writer;
Init ();
}
#endregion
#region Private Methods
private void DoValidation (Condition cond)
{
if (! context.ExpectingValue)
context.Count++;
if (! validate)
return;
if (has_reached_end)
throw new JsonException (
"A complete JSON symbol has already been written");
switch (cond) {
case Condition.InArray:
if (! context.InArray)
throw new JsonException (
"Can't close an array here");
break;
case Condition.InObject:
if (! context.InObject || context.ExpectingValue)
throw new JsonException (
"Can't close an object here");
break;
case Condition.NotAProperty:
if (context.InObject && ! context.ExpectingValue)
throw new JsonException (
"Expected a property");
break;
case Condition.Property:
if (! context.InObject || context.ExpectingValue)
throw new JsonException (
"Can't add a property here");
break;
case Condition.Value:
if (! context.InArray &&
(! context.InObject || ! context.ExpectingValue))
throw new JsonException (
"Can't add a value here");
break;
}
}
private void Init ()
{
has_reached_end = false;
hex_seq = new char[4];
indentation = 0;
indent_value = 4;
pretty_print = false;
validate = true;
ctx_stack = new Stack<WriterContext> ();
context = new WriterContext ();
ctx_stack.Push (context);
}
private static void IntToHex (int n, char[] hex)
{
int num;
for (int i = 0; i < 4; i++) {
num = n % 16;
if (num < 10)
hex[3 - i] = (char) ('0' + num);
else
hex[3 - i] = (char) ('A' + (num - 10));
n >>= 4;
}
}
private void Indent ()
{
if (pretty_print)
indentation += indent_value;
}
private void Put (string str)
{
if (pretty_print && ! context.ExpectingValue)
for (int i = 0; i < indentation; i++)
writer.Write (' ');
writer.Write (str);
}
private void PutNewline ()
{
PutNewline (true);
}
private void PutNewline (bool add_comma)
{
if (add_comma && ! context.ExpectingValue &&
context.Count > 1)
writer.Write (',');
if (pretty_print && ! context.ExpectingValue)
writer.Write ('\n');
}
private void PutString (string str)
{
Put (String.Empty);
writer.Write ('"');
int n = str.Length;
for (int i = 0; i < n; i++) {
switch (str[i]) {
case '\n':
writer.Write ("\\n");
continue;
case '\r':
writer.Write ("\\r");
continue;
case '\t':
writer.Write ("\\t");
continue;
case '"':
case '\\':
writer.Write ('\\');
writer.Write (str[i]);
continue;
case '\f':
writer.Write ("\\f");
continue;
case '\b':
writer.Write ("\\b");
continue;
}
if ((int) str[i] >= 32 && (int) str[i] <= 126) {
writer.Write (str[i]);
continue;
}
// Default, turn into a \uXXXX sequence
IntToHex ((int) str[i], hex_seq);
writer.Write ("\\u");
writer.Write (hex_seq);
}
writer.Write ('"');
}
private void Unindent ()
{
if (pretty_print)
indentation -= indent_value;
}
#endregion
public override string ToString ()
{
if (inst_string_builder == null)
return String.Empty;
return inst_string_builder.ToString ();
}
public void Reset ()
{
has_reached_end = false;
ctx_stack.Clear ();
context = new WriterContext ();
ctx_stack.Push (context);
if (inst_string_builder != null)
inst_string_builder.Remove (0, inst_string_builder.Length);
}
public void Write (bool boolean)
{
DoValidation (Condition.Value);
PutNewline ();
Put (boolean ? "true" : "false");
context.ExpectingValue = false;
}
public void Write (decimal number)
{
DoValidation (Condition.Value);
PutNewline ();
Put (Convert.ToString (number, number_format));
context.ExpectingValue = false;
}
public void Write (double number)
{
DoValidation (Condition.Value);
PutNewline ();
if (double.IsNaN(number) || double.IsInfinity(number))
Put("null");
else
{
string str = Convert.ToString(number, number_format);
Put(str);
if (str.IndexOf('.') == -1 && str.IndexOf('E') == -1)
writer.Write(".0");
}
context.ExpectingValue = false;
}
public void Write (int number)
{
DoValidation (Condition.Value);
PutNewline ();
Put (Convert.ToString (number, number_format));
context.ExpectingValue = false;
}
public void Write (long number)
{
DoValidation (Condition.Value);
PutNewline ();
Put (Convert.ToString (number, number_format));
context.ExpectingValue = false;
}
public void Write (string str)
{
DoValidation (Condition.Value);
PutNewline ();
if (str == null)
Put ("null");
else
PutString (str);
context.ExpectingValue = false;
}
public void Write (ulong number)
{
DoValidation (Condition.Value);
PutNewline ();
Put (Convert.ToString (number, number_format));
context.ExpectingValue = false;
}
public void WriteArrayEnd ()
{
DoValidation (Condition.InArray);
PutNewline (false);
ctx_stack.Pop ();
if (ctx_stack.Count == 1)
has_reached_end = true;
else {
context = ctx_stack.Peek ();
context.ExpectingValue = false;
}
Unindent ();
Put ("]");
}
public void WriteArrayStart ()
{
DoValidation (Condition.NotAProperty);
PutNewline ();
Put ("[");
context = new WriterContext ();
context.InArray = true;
ctx_stack.Push (context);
Indent ();
}
public void WriteObjectEnd ()
{
DoValidation (Condition.InObject);
PutNewline (false);
ctx_stack.Pop ();
if (ctx_stack.Count == 1)
has_reached_end = true;
else {
context = ctx_stack.Peek ();
context.ExpectingValue = false;
}
Unindent ();
Put ("}");
}
public void WriteObjectStart ()
{
DoValidation (Condition.NotAProperty);
PutNewline ();
Put ("{");
context = new WriterContext ();
context.InObject = true;
ctx_stack.Push (context);
Indent ();
}
public void WritePropertyName (string property_name)
{
DoValidation (Condition.Property);
PutNewline ();
PutString (property_name);
if (pretty_print) {
if (property_name.Length > context.Padding)
context.Padding = property_name.Length;
for (int i = context.Padding - property_name.Length;
i >= 0; i--)
writer.Write (' ');
writer.Write (": ");
} else
writer.Write (':');
context.ExpectingValue = true;
}
}
}
+910
View File
@@ -0,0 +1,910 @@
#region Header
/*
* Lexer.cs
* JSON lexer implementation based on a finite state machine.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace LitJson
{
internal class FsmContext
{
public bool Return;
public int NextState;
public Lexer L;
public int StateStack;
}
internal class Lexer
{
#region Fields
private delegate bool StateHandler (FsmContext ctx);
private static int[] fsm_return_table;
private static StateHandler[] fsm_handler_table;
private bool allow_comments;
private bool allow_single_quoted_strings;
private bool end_of_input;
private FsmContext fsm_context;
private int input_buffer;
private int input_char;
private TextReader reader;
private int state;
private StringBuilder string_buffer;
private string string_value;
private int token;
private int unichar;
#endregion
#region Properties
public bool AllowComments {
get { return allow_comments; }
set { allow_comments = value; }
}
public bool AllowSingleQuotedStrings {
get { return allow_single_quoted_strings; }
set { allow_single_quoted_strings = value; }
}
public bool EndOfInput {
get { return end_of_input; }
}
public int Token {
get { return token; }
}
public string StringValue {
get { return string_value; }
}
#endregion
#region Constructors
static Lexer ()
{
PopulateFsmTables ();
}
public Lexer (TextReader reader)
{
allow_comments = true;
allow_single_quoted_strings = true;
input_buffer = 0;
string_buffer = new StringBuilder (128);
state = 1;
end_of_input = false;
this.reader = reader;
fsm_context = new FsmContext ();
fsm_context.L = this;
}
#endregion
#region Static Methods
private static int HexValue (int digit)
{
switch (digit) {
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
case 'c':
case 'C':
return 12;
case 'd':
case 'D':
return 13;
case 'e':
case 'E':
return 14;
case 'f':
case 'F':
return 15;
default:
return digit - '0';
}
}
private static void PopulateFsmTables ()
{
fsm_handler_table = new StateHandler[28] {
State1,
State2,
State3,
State4,
State5,
State6,
State7,
State8,
State9,
State10,
State11,
State12,
State13,
State14,
State15,
State16,
State17,
State18,
State19,
State20,
State21,
State22,
State23,
State24,
State25,
State26,
State27,
State28
};
fsm_return_table = new int[28] {
(int) ParserToken.Char,
0,
(int) ParserToken.Number,
(int) ParserToken.Number,
0,
(int) ParserToken.Number,
0,
(int) ParserToken.Number,
0,
0,
(int) ParserToken.True,
0,
0,
0,
(int) ParserToken.False,
0,
0,
(int) ParserToken.Null,
(int) ParserToken.CharSeq,
(int) ParserToken.Char,
0,
0,
(int) ParserToken.CharSeq,
(int) ParserToken.Char,
0,
0,
0,
0
};
}
private static char ProcessEscChar (int esc_char)
{
switch (esc_char) {
case '"':
case '\'':
case '\\':
case '/':
return Convert.ToChar (esc_char);
case 'n':
return '\n';
case 't':
return '\t';
case 'r':
return '\r';
case 'b':
return '\b';
case 'f':
return '\f';
default:
// Unreachable
return '?';
}
}
private static bool State1 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char == ' ' ||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r')
continue;
if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 3;
return true;
}
switch (ctx.L.input_char) {
case '"':
ctx.NextState = 19;
ctx.Return = true;
return true;
case ',':
case ':':
case '[':
case ']':
case '{':
case '}':
ctx.NextState = 1;
ctx.Return = true;
return true;
case '-':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 2;
return true;
case '0':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 4;
return true;
case 'f':
ctx.NextState = 12;
return true;
case 'n':
ctx.NextState = 16;
return true;
case 't':
ctx.NextState = 9;
return true;
case '\'':
if (! ctx.L.allow_single_quoted_strings)
return false;
ctx.L.input_char = '"';
ctx.NextState = 23;
ctx.Return = true;
return true;
case '/':
if (! ctx.L.allow_comments)
return false;
ctx.NextState = 25;
return true;
default:
return false;
}
}
return true;
}
private static bool State2 (FsmContext ctx)
{
ctx.L.GetChar ();
if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 3;
return true;
}
switch (ctx.L.input_char) {
case '0':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 4;
return true;
default:
return false;
}
}
private static bool State3 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
continue;
}
if (ctx.L.input_char == ' ' ||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
ctx.Return = true;
ctx.NextState = 1;
return true;
}
switch (ctx.L.input_char) {
case ',':
case ']':
case '}':
ctx.L.UngetChar ();
ctx.Return = true;
ctx.NextState = 1;
return true;
case '.':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 5;
return true;
case 'e':
case 'E':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 7;
return true;
default:
return false;
}
}
return true;
}
private static bool State4 (FsmContext ctx)
{
ctx.L.GetChar ();
if (ctx.L.input_char == ' ' ||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
ctx.Return = true;
ctx.NextState = 1;
return true;
}
switch (ctx.L.input_char) {
case ',':
case ']':
case '}':
ctx.L.UngetChar ();
ctx.Return = true;
ctx.NextState = 1;
return true;
case '.':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 5;
return true;
case 'e':
case 'E':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 7;
return true;
default:
return false;
}
}
private static bool State5 (FsmContext ctx)
{
ctx.L.GetChar ();
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 6;
return true;
}
return false;
}
private static bool State6 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
continue;
}
if (ctx.L.input_char == ' ' ||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
ctx.Return = true;
ctx.NextState = 1;
return true;
}
switch (ctx.L.input_char) {
case ',':
case ']':
case '}':
ctx.L.UngetChar ();
ctx.Return = true;
ctx.NextState = 1;
return true;
case 'e':
case 'E':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 7;
return true;
default:
return false;
}
}
return true;
}
private static bool State7 (FsmContext ctx)
{
ctx.L.GetChar ();
if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 8;
return true;
}
switch (ctx.L.input_char) {
case '+':
case '-':
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
ctx.NextState = 8;
return true;
default:
return false;
}
}
private static bool State8 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') {
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
continue;
}
if (ctx.L.input_char == ' ' ||
ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') {
ctx.Return = true;
ctx.NextState = 1;
return true;
}
switch (ctx.L.input_char) {
case ',':
case ']':
case '}':
ctx.L.UngetChar ();
ctx.Return = true;
ctx.NextState = 1;
return true;
default:
return false;
}
}
return true;
}
private static bool State9 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'r':
ctx.NextState = 10;
return true;
default:
return false;
}
}
private static bool State10 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'u':
ctx.NextState = 11;
return true;
default:
return false;
}
}
private static bool State11 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'e':
ctx.Return = true;
ctx.NextState = 1;
return true;
default:
return false;
}
}
private static bool State12 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'a':
ctx.NextState = 13;
return true;
default:
return false;
}
}
private static bool State13 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'l':
ctx.NextState = 14;
return true;
default:
return false;
}
}
private static bool State14 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 's':
ctx.NextState = 15;
return true;
default:
return false;
}
}
private static bool State15 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'e':
ctx.Return = true;
ctx.NextState = 1;
return true;
default:
return false;
}
}
private static bool State16 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'u':
ctx.NextState = 17;
return true;
default:
return false;
}
}
private static bool State17 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'l':
ctx.NextState = 18;
return true;
default:
return false;
}
}
private static bool State18 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'l':
ctx.Return = true;
ctx.NextState = 1;
return true;
default:
return false;
}
}
private static bool State19 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
switch (ctx.L.input_char) {
case '"':
ctx.L.UngetChar ();
ctx.Return = true;
ctx.NextState = 20;
return true;
case '\\':
ctx.StateStack = 19;
ctx.NextState = 21;
return true;
default:
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
continue;
}
}
return true;
}
private static bool State20 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case '"':
ctx.Return = true;
ctx.NextState = 1;
return true;
default:
return false;
}
}
private static bool State21 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case 'u':
ctx.NextState = 22;
return true;
case '"':
case '\'':
case '/':
case '\\':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
ctx.L.string_buffer.Append (
ProcessEscChar (ctx.L.input_char));
ctx.NextState = ctx.StateStack;
return true;
default:
return false;
}
}
private static bool State22 (FsmContext ctx)
{
int counter = 0;
int mult = 4096;
ctx.L.unichar = 0;
while (ctx.L.GetChar ()) {
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' ||
ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' ||
ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') {
ctx.L.unichar += HexValue (ctx.L.input_char) * mult;
counter++;
mult /= 16;
if (counter == 4) {
ctx.L.string_buffer.Append (
Convert.ToChar (ctx.L.unichar));
ctx.NextState = ctx.StateStack;
return true;
}
continue;
}
return false;
}
return true;
}
private static bool State23 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
switch (ctx.L.input_char) {
case '\'':
ctx.L.UngetChar ();
ctx.Return = true;
ctx.NextState = 24;
return true;
case '\\':
ctx.StateStack = 23;
ctx.NextState = 21;
return true;
default:
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
continue;
}
}
return true;
}
private static bool State24 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case '\'':
ctx.L.input_char = '"';
ctx.Return = true;
ctx.NextState = 1;
return true;
default:
return false;
}
}
private static bool State25 (FsmContext ctx)
{
ctx.L.GetChar ();
switch (ctx.L.input_char) {
case '*':
ctx.NextState = 27;
return true;
case '/':
ctx.NextState = 26;
return true;
default:
return false;
}
}
private static bool State26 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char == '\n') {
ctx.NextState = 1;
return true;
}
}
return true;
}
private static bool State27 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char == '*') {
ctx.NextState = 28;
return true;
}
}
return true;
}
private static bool State28 (FsmContext ctx)
{
while (ctx.L.GetChar ()) {
if (ctx.L.input_char == '*')
continue;
if (ctx.L.input_char == '/') {
ctx.NextState = 1;
return true;
}
ctx.NextState = 27;
return true;
}
return true;
}
#endregion
private bool GetChar ()
{
if ((input_char = NextChar ()) != -1)
return true;
end_of_input = true;
return false;
}
private int NextChar ()
{
if (input_buffer != 0) {
int tmp = input_buffer;
input_buffer = 0;
return tmp;
}
return reader.Read ();
}
public bool NextToken ()
{
StateHandler handler;
fsm_context.Return = false;
while (true) {
handler = fsm_handler_table[state - 1];
if (! handler (fsm_context))
throw new JsonException (input_char);
if (end_of_input)
return false;
if (fsm_context.Return) {
string_value = string_buffer.ToString ();
string_buffer.Remove (0, string_buffer.Length);
token = fsm_return_table[state - 1];
if (token == (int) ParserToken.Char)
token = input_char;
state = fsm_context.NextState;
return true;
}
state = fsm_context.NextState;
}
}
private void UngetChar ()
{
input_buffer = input_char;
}
}
}
@@ -0,0 +1,200 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using LitJson;
namespace OpenMetaverse.StructuredData
{
public static partial class OSDParser
{
public static OSD DeserializeJson(Stream json)
{
using (StreamReader streamReader = new StreamReader(json))
{
JsonReader reader = new JsonReader(streamReader);
return DeserializeJson(JsonMapper.ToObject(reader));
}
}
public static OSD DeserializeJson(string json)
{
return DeserializeJson(JsonMapper.ToObject(json));
}
public static OSD DeserializeJson(JsonData json)
{
if (json == null) return new OSD();
switch (json.GetJsonType())
{
case JsonType.Boolean:
return OSD.FromBoolean((bool)json);
case JsonType.Int:
return OSD.FromInteger((int)json);
case JsonType.Long:
return OSD.FromLong((long)json);
case JsonType.Double:
return OSD.FromReal((double)json);
case JsonType.String:
string str = (string)json;
if (String.IsNullOrEmpty(str))
return new OSD();
else
return OSD.FromString(str);
case JsonType.Array:
OSDArray array = new OSDArray(json.Count);
for (int i = 0; i < json.Count; i++)
array.Add(DeserializeJson(json[i]));
return array;
case JsonType.Object:
OSDMap map = new OSDMap(json.Count);
IDictionaryEnumerator e = ((IOrderedDictionary)json).GetEnumerator();
while (e.MoveNext())
map.Add((string)e.Key, DeserializeJson((JsonData)e.Value));
return map;
case JsonType.None:
default:
return new OSD();
}
}
public static string SerializeJsonString(OSD osd)
{
return SerializeJson(osd, false).ToJson();
}
public static string SerializeJsonString(OSD osd, bool preserveDefaults)
{
return SerializeJson(osd, preserveDefaults).ToJson();
}
public static void SerializeJsonString(OSD osd, bool preserveDefaults, ref JsonWriter writer)
{
SerializeJson(osd, preserveDefaults).ToJson(writer);
}
public static JsonData SerializeJson(OSD osd, bool preserveDefaults)
{
switch (osd.Type)
{
case OSDType.Boolean:
return new JsonData(osd.AsBoolean());
case OSDType.Integer:
return new JsonData(osd.AsInteger());
case OSDType.Real:
return new JsonData(osd.AsReal());
case OSDType.String:
case OSDType.Date:
case OSDType.URI:
case OSDType.UUID:
return new JsonData(osd.AsString());
case OSDType.Binary:
byte[] binary = osd.AsBinary();
JsonData jsonbinarray = new JsonData();
jsonbinarray.SetJsonType(JsonType.Array);
for (int i = 0; i < binary.Length; i++)
jsonbinarray.Add(new JsonData(binary[i]));
return jsonbinarray;
case OSDType.Array:
JsonData jsonarray = new JsonData();
jsonarray.SetJsonType(JsonType.Array);
OSDArray array = (OSDArray)osd;
for (int i = 0; i < array.Count; i++)
jsonarray.Add(SerializeJson(array[i], preserveDefaults));
return jsonarray;
case OSDType.Map:
JsonData jsonmap = new JsonData();
jsonmap.SetJsonType(JsonType.Object);
OSDMap map = (OSDMap)osd;
foreach (KeyValuePair<string, OSD> kvp in map)
{
JsonData data;
if (preserveDefaults)
data = SerializeJson(kvp.Value, preserveDefaults);
else
data = SerializeJsonNoDefaults(kvp.Value);
if (data != null)
jsonmap[kvp.Key] = data;
}
return jsonmap;
case OSDType.Unknown:
default:
return new JsonData(null);
}
}
private static JsonData SerializeJsonNoDefaults(OSD osd)
{
switch (osd.Type)
{
case OSDType.Boolean:
bool b = osd.AsBoolean();
if (!b)
return null;
return new JsonData(b);
case OSDType.Integer:
int v = osd.AsInteger();
if (v == 0)
return null;
return new JsonData(v);
case OSDType.Real:
double d = osd.AsReal();
if (d == 0.0d)
return null;
return new JsonData(d);
case OSDType.String:
case OSDType.Date:
case OSDType.URI:
string str = osd.AsString();
if (String.IsNullOrEmpty(str))
return null;
return new JsonData(str);
case OSDType.UUID:
UUID uuid = osd.AsUUID();
if (uuid == UUID.Zero)
return null;
return new JsonData(uuid.ToString());
case OSDType.Binary:
byte[] binary = osd.AsBinary();
if (binary == Utils.EmptyBytes)
return null;
JsonData jsonbinarray = new JsonData();
jsonbinarray.SetJsonType(JsonType.Array);
for (int i = 0; i < binary.Length; i++)
jsonbinarray.Add(new JsonData(binary[i]));
return jsonbinarray;
case OSDType.Array:
JsonData jsonarray = new JsonData();
jsonarray.SetJsonType(JsonType.Array);
OSDArray array = (OSDArray)osd;
for (int i = 0; i < array.Count; i++)
jsonarray.Add(SerializeJson(array[i], false));
return jsonarray;
case OSDType.Map:
JsonData jsonmap = new JsonData();
jsonmap.SetJsonType(JsonType.Object);
OSDMap map = (OSDMap)osd;
foreach (KeyValuePair<string, OSD> kvp in map)
{
JsonData data = SerializeJsonNoDefaults(kvp.Value);
if (data != null)
jsonmap[kvp.Key] = data;
}
return jsonmap;
case OSDType.Unknown:
default:
return null;
}
}
}
}
@@ -0,0 +1,44 @@
#region Header
/*
* ParserToken.cs
* Internal representation of the tokens used by the lexer and the parser.
*
* The authors disclaim copyright to this source code. For more details, see
* the COPYING file included with this distribution.
*/
#endregion
namespace LitJson
{
internal enum ParserToken
{
// Lexer tokens
None = System.Char.MaxValue + 1,
Number,
True,
False,
Null,
CharSeq,
// Single char
Char,
// Parser Rules
Text,
Object,
ObjectPrime,
Pair,
PairRest,
Array,
ArrayPrime,
Value,
ValueRest,
String,
// End of input
End,
// The empty rule
Epsilon
}
}
@@ -0,0 +1,502 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.
*/
/*
*
* This implementation is based upon the description at
*
* http://wiki.secondlife.com/wiki/LLSD
*
* and (partially) tested against the (supposed) reference implementation at
*
* http://svn.secondlife.com/svn/linden/release/indra/lib/python/indra/base/osd.py
*
*/
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace OpenMetaverse.StructuredData
{
/// <summary>
///
/// </summary>
public static partial class OSDParser
{
private const int initialBufferSize = 128;
private const int int32Length = 4;
private const int doubleLength = 8;
private const string llsdBinaryHead = "<? llsd/binary ?>";
private const string llsdBinaryHead2 = "<?llsd/binary?>";
private const byte undefBinaryValue = (byte)'!';
private const byte trueBinaryValue = (byte)'1';
private const byte falseBinaryValue = (byte)'0';
private const byte integerBinaryMarker = (byte)'i';
private const byte realBinaryMarker = (byte)'r';
private const byte uuidBinaryMarker = (byte)'u';
private const byte binaryBinaryMarker = (byte)'b';
private const byte stringBinaryMarker = (byte)'s';
private const byte uriBinaryMarker = (byte)'l';
private const byte dateBinaryMarker = (byte)'d';
private const byte arrayBeginBinaryMarker = (byte)'[';
private const byte arrayEndBinaryMarker = (byte)']';
private const byte mapBeginBinaryMarker = (byte)'{';
private const byte mapEndBinaryMarker = (byte)'}';
private const byte keyBinaryMarker = (byte)'k';
private static readonly byte[] llsdBinaryHeadBytes = Encoding.ASCII.GetBytes(llsdBinaryHead2);
/// <summary>
/// Deserializes binary LLSD
/// </summary>
/// <param name="binaryData">Serialized data</param>
/// <returns>OSD containting deserialized data</returns>
public static OSD DeserializeLLSDBinary(byte[] binaryData)
{
MemoryStream stream = new MemoryStream(binaryData);
OSD osd = DeserializeLLSDBinary(stream);
stream.Close();
return osd;
}
/// <summary>
/// Deserializes binary LLSD
/// </summary>
/// <param name="stream">Stream to read the data from</param>
/// <returns>OSD containting deserialized data</returns>
public static OSD DeserializeLLSDBinary(Stream stream)
{
if (!stream.CanSeek)
throw new OSDException("Cannot deserialize binary LLSD from unseekable streams");
SkipWhiteSpace(stream);
if (!FindString(stream, llsdBinaryHead) && !FindString(stream, llsdBinaryHead2))
{
//throw new OSDException("Failed to decode binary LLSD");
}
SkipWhiteSpace(stream);
return ParseLLSDBinaryElement(stream);
}
/// <summary>
/// Serializes OSD to binary format. It does no prepend header
/// </summary>
/// <param name="osd">OSD to serialize</param>
/// <returns>Serialized data</returns>
public static byte[] SerializeLLSDBinary(OSD osd)
{
return SerializeLLSDBinary(osd, true);
}
/// <summary>
/// Serializes OSD to binary format
/// </summary>
/// <param name="osd">OSD to serialize</param>
/// <param name="prependHeader"></param>
/// <returns>Serialized data</returns>
public static byte[] SerializeLLSDBinary(OSD osd, bool prependHeader)
{
MemoryStream stream = SerializeLLSDBinaryStream(osd, prependHeader);
byte[] binaryData = stream.ToArray();
stream.Close();
return binaryData;
}
/// <summary>
/// Serializes OSD to binary format. It does no prepend header
/// </summary>
/// <param name="data">OSD to serialize</param>
/// <returns>Serialized data</returns>
public static MemoryStream SerializeLLSDBinaryStream(OSD data)
{
return SerializeLLSDBinaryStream(data, true);
}
/// <summary>
/// Serializes OSD to binary format
/// </summary>
/// <param name="data">OSD to serialize</param>
/// <param name="prependHeader"></param>
/// <returns>Serialized data</returns>
public static MemoryStream SerializeLLSDBinaryStream(OSD data, bool prependHeader)
{
MemoryStream stream = new MemoryStream(initialBufferSize);
if (prependHeader)
{
stream.Write(llsdBinaryHeadBytes, 0, llsdBinaryHeadBytes.Length);
stream.WriteByte((byte)'\n');
}
SerializeLLSDBinaryElement(stream, data);
return stream;
}
private static void SerializeLLSDBinaryElement(MemoryStream stream, OSD osd)
{
switch (osd.Type)
{
case OSDType.Unknown:
stream.WriteByte(undefBinaryValue);
break;
case OSDType.Boolean:
stream.Write(osd.AsBinary(), 0, 1);
break;
case OSDType.Integer:
stream.WriteByte(integerBinaryMarker);
stream.Write(osd.AsBinary(), 0, int32Length);
break;
case OSDType.Real:
stream.WriteByte(realBinaryMarker);
stream.Write(osd.AsBinary(), 0, doubleLength);
break;
case OSDType.UUID:
stream.WriteByte(uuidBinaryMarker);
stream.Write(osd.AsBinary(), 0, 16);
break;
case OSDType.String:
stream.WriteByte(stringBinaryMarker);
byte[] rawString = osd.AsBinary();
byte[] stringLengthNetEnd = HostToNetworkIntBytes(rawString.Length);
stream.Write(stringLengthNetEnd, 0, int32Length);
stream.Write(rawString, 0, rawString.Length);
break;
case OSDType.Binary:
stream.WriteByte(binaryBinaryMarker);
byte[] rawBinary = osd.AsBinary();
byte[] binaryLengthNetEnd = HostToNetworkIntBytes(rawBinary.Length);
stream.Write(binaryLengthNetEnd, 0, int32Length);
stream.Write(rawBinary, 0, rawBinary.Length);
break;
case OSDType.Date:
stream.WriteByte(dateBinaryMarker);
stream.Write(osd.AsBinary(), 0, doubleLength);
break;
case OSDType.URI:
stream.WriteByte(uriBinaryMarker);
byte[] rawURI = osd.AsBinary();
byte[] uriLengthNetEnd = HostToNetworkIntBytes(rawURI.Length);
stream.Write(uriLengthNetEnd, 0, int32Length);
stream.Write(rawURI, 0, rawURI.Length);
break;
case OSDType.Array:
SerializeLLSDBinaryArray(stream, (OSDArray)osd);
break;
case OSDType.Map:
SerializeLLSDBinaryMap(stream, (OSDMap)osd);
break;
default:
throw new OSDException("Binary serialization: Not existing element discovered.");
}
}
private static void SerializeLLSDBinaryArray(MemoryStream stream, OSDArray osdArray)
{
stream.WriteByte(arrayBeginBinaryMarker);
byte[] binaryNumElementsHostEnd = HostToNetworkIntBytes(osdArray.Count);
stream.Write(binaryNumElementsHostEnd, 0, int32Length);
foreach (OSD osd in osdArray)
{
SerializeLLSDBinaryElement(stream, osd);
}
stream.WriteByte(arrayEndBinaryMarker);
}
private static void SerializeLLSDBinaryMap(MemoryStream stream, OSDMap osdMap)
{
stream.WriteByte(mapBeginBinaryMarker);
byte[] binaryNumElementsNetEnd = HostToNetworkIntBytes(osdMap.Count);
stream.Write(binaryNumElementsNetEnd, 0, int32Length);
foreach (KeyValuePair<string, OSD> kvp in osdMap)
{
stream.WriteByte(keyBinaryMarker);
byte[] binaryKey = Encoding.UTF8.GetBytes(kvp.Key);
byte[] binaryKeyLength = HostToNetworkIntBytes(binaryKey.Length);
stream.Write(binaryKeyLength, 0, int32Length);
stream.Write(binaryKey, 0, binaryKey.Length);
SerializeLLSDBinaryElement(stream, kvp.Value);
}
stream.WriteByte(mapEndBinaryMarker);
}
private static OSD ParseLLSDBinaryElement(Stream stream)
{
SkipWhiteSpace(stream);
OSD osd;
int marker = stream.ReadByte();
if (marker < 0)
throw new OSDException("Binary LLSD parsing: Unexpected end of stream.");
switch ((byte)marker)
{
case undefBinaryValue:
osd = new OSD();
break;
case trueBinaryValue:
osd = OSD.FromBoolean(true);
break;
case falseBinaryValue:
osd = OSD.FromBoolean(false);
break;
case integerBinaryMarker:
int integer = NetworkToHostInt(ConsumeBytes(stream, int32Length));
osd = OSD.FromInteger(integer);
break;
case realBinaryMarker:
double dbl = NetworkToHostDouble(ConsumeBytes(stream, doubleLength));
osd = OSD.FromReal(dbl);
break;
case uuidBinaryMarker:
osd = OSD.FromUUID(new UUID(ConsumeBytes(stream, 16), 0));
break;
case binaryBinaryMarker:
int binaryLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
osd = OSD.FromBinary(ConsumeBytes(stream, binaryLength));
break;
case stringBinaryMarker:
int stringLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
string ss = Encoding.UTF8.GetString(ConsumeBytes(stream, stringLength));
osd = OSD.FromString(ss);
break;
case uriBinaryMarker:
int uriLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
string sUri = Encoding.UTF8.GetString(ConsumeBytes(stream, uriLength));
Uri uri;
try
{
uri = new Uri(sUri, UriKind.RelativeOrAbsolute);
}
catch
{
throw new OSDException("Binary LLSD parsing: Invalid Uri format detected.");
}
osd = OSD.FromUri(uri);
break;
case dateBinaryMarker:
double timestamp = Utils.BytesToDouble(ConsumeBytes(stream, doubleLength), 0);
DateTime dateTime = DateTime.SpecifyKind(Utils.Epoch, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(timestamp);
osd = OSD.FromDate(dateTime.ToLocalTime());
break;
case arrayBeginBinaryMarker:
osd = ParseLLSDBinaryArray(stream);
break;
case mapBeginBinaryMarker:
osd = ParseLLSDBinaryMap(stream);
break;
default:
throw new OSDException("Binary LLSD parsing: Unknown type marker.");
}
return osd;
}
private static OSD ParseLLSDBinaryArray(Stream stream)
{
int numElements = NetworkToHostInt(ConsumeBytes(stream, int32Length));
int crrElement = 0;
OSDArray osdArray = new OSDArray();
while (crrElement < numElements)
{
osdArray.Add(ParseLLSDBinaryElement(stream));
crrElement++;
}
if (!FindByte(stream, arrayEndBinaryMarker))
throw new OSDException("Binary LLSD parsing: Missing end marker in array.");
return (OSD)osdArray;
}
private static OSD ParseLLSDBinaryMap(Stream stream)
{
int numElements = NetworkToHostInt(ConsumeBytes(stream, int32Length));
int crrElement = 0;
OSDMap osdMap = new OSDMap();
while (crrElement < numElements)
{
if (!FindByte(stream, keyBinaryMarker))
throw new OSDException("Binary LLSD parsing: Missing key marker in map.");
int keyLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
string key = Encoding.UTF8.GetString(ConsumeBytes(stream, keyLength));
osdMap[key] = ParseLLSDBinaryElement(stream);
crrElement++;
}
if (!FindByte(stream, mapEndBinaryMarker))
throw new OSDException("Binary LLSD parsing: Missing end marker in map.");
return (OSD)osdMap;
}
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
public static void SkipWhiteSpace(Stream stream)
{
int bt;
while (((bt = stream.ReadByte()) > 0) &&
((byte)bt == ' ' || (byte)bt == '\t' ||
(byte)bt == '\n' || (byte)bt == '\r')
)
{
}
stream.Seek(-1, SeekOrigin.Current);
}
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
/// <param name="toFind"></param>
/// <returns></returns>
public static bool FindByte(Stream stream, byte toFind)
{
int bt = stream.ReadByte();
if (bt < 0)
return false;
if ((byte)bt == toFind)
return true;
else
{
stream.Seek(-1L, SeekOrigin.Current);
return false;
}
}
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
/// <param name="toFind"></param>
/// <returns></returns>
public static bool FindString(Stream stream, string toFind)
{
int lastIndexToFind = toFind.Length - 1;
int crrIndex = 0;
bool found = true;
int bt;
long lastPosition = stream.Position;
while (found &&
((bt = stream.ReadByte()) > 0) &&
(crrIndex <= lastIndexToFind)
)
{
if (toFind[crrIndex].ToString().Equals(((char)bt).ToString(), StringComparison.InvariantCultureIgnoreCase))
{
found = true;
crrIndex++;
}
else
found = false;
}
if (found && crrIndex > lastIndexToFind)
{
stream.Seek(-1L, SeekOrigin.Current);
return true;
}
else
{
stream.Position = lastPosition;
return false;
}
}
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
/// <param name="consumeBytes"></param>
/// <returns></returns>
public static byte[] ConsumeBytes(Stream stream, int consumeBytes)
{
byte[] bytes = new byte[consumeBytes];
if (stream.Read(bytes, 0, consumeBytes) < consumeBytes)
throw new OSDException("Binary LLSD parsing: Unexpected end of stream.");
return bytes;
}
/// <summary>
///
/// </summary>
/// <param name="binaryNetEnd"></param>
/// <returns></returns>
public static int NetworkToHostInt(byte[] binaryNetEnd)
{
if (binaryNetEnd == null)
return -1;
int intNetEnd = BitConverter.ToInt32(binaryNetEnd, 0);
int intHostEnd = System.Net.IPAddress.NetworkToHostOrder(intNetEnd);
return intHostEnd;
}
/// <summary>
///
/// </summary>
/// <param name="binaryNetEnd"></param>
/// <returns></returns>
public static double NetworkToHostDouble(byte[] binaryNetEnd)
{
if (binaryNetEnd == null)
return -1d;
long longNetEnd = BitConverter.ToInt64(binaryNetEnd, 0);
long longHostEnd = System.Net.IPAddress.NetworkToHostOrder(longNetEnd);
byte[] binaryHostEnd = BitConverter.GetBytes(longHostEnd);
double doubleHostEnd = BitConverter.ToDouble(binaryHostEnd, 0);
return doubleHostEnd;
}
/// <summary>
///
/// </summary>
/// <param name="intHostEnd"></param>
/// <returns></returns>
public static byte[] HostToNetworkIntBytes(int intHostEnd)
{
int intNetEnd = System.Net.IPAddress.HostToNetworkOrder(intHostEnd);
byte[] bytesNetEnd = BitConverter.GetBytes(intNetEnd);
return bytesNetEnd;
}
}
}
@@ -0,0 +1,774 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using System.IO;
namespace OpenMetaverse.StructuredData
{
/// <summary>
///
/// </summary>
public static partial class OSDParser
{
private const string baseIndent = " ";
private const char undefNotationValue = '!';
private const char trueNotationValueOne = '1';
private const char trueNotationValueTwo = 't';
private static readonly char[] trueNotationValueTwoFull = { 't', 'r', 'u', 'e' };
private const char trueNotationValueThree = 'T';
private static readonly char[] trueNotationValueThreeFull = { 'T', 'R', 'U', 'E' };
private const char falseNotationValueOne = '0';
private const char falseNotationValueTwo = 'f';
private static readonly char[] falseNotationValueTwoFull = { 'f', 'a', 'l', 's', 'e' };
private const char falseNotationValueThree = 'F';
private static readonly char[] falseNotationValueThreeFull = { 'F', 'A', 'L', 'S', 'E' };
private const char integerNotationMarker = 'i';
private const char realNotationMarker = 'r';
private const char uuidNotationMarker = 'u';
private const char binaryNotationMarker = 'b';
private const char stringNotationMarker = 's';
private const char uriNotationMarker = 'l';
private const char dateNotationMarker = 'd';
private const char arrayBeginNotationMarker = '[';
private const char arrayEndNotationMarker = ']';
private const char mapBeginNotationMarker = '{';
private const char mapEndNotationMarker = '}';
private const char kommaNotationDelimiter = ',';
private const char keyNotationDelimiter = ':';
private const char sizeBeginNotationMarker = '(';
private const char sizeEndNotationMarker = ')';
private const char doubleQuotesNotationMarker = '"';
private const char singleQuotesNotationMarker = '\'';
public static OSD DeserializeLLSDNotation(string notationData)
{
StringReader reader = new StringReader(notationData);
OSD osd = DeserializeLLSDNotation(reader);
reader.Close();
return osd;
}
public static OSD DeserializeLLSDNotation(StringReader reader)
{
OSD osd = DeserializeLLSDNotationElement(reader);
return osd;
}
public static string SerializeLLSDNotation(OSD osd)
{
StringWriter writer = SerializeLLSDNotationStream(osd);
string s = writer.ToString();
writer.Close();
return s;
}
public static StringWriter SerializeLLSDNotationStream(OSD osd)
{
StringWriter writer = new StringWriter();
SerializeLLSDNotationElement(writer, osd);
return writer;
}
public static string SerializeLLSDNotationFormatted(OSD osd)
{
StringWriter writer = SerializeLLSDNotationStreamFormatted(osd);
string s = writer.ToString();
writer.Close();
return s;
}
public static StringWriter SerializeLLSDNotationStreamFormatted(OSD osd)
{
StringWriter writer = new StringWriter();
string indent = String.Empty;
SerializeLLSDNotationElementFormatted(writer, indent, osd);
return writer;
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
private static OSD DeserializeLLSDNotationElement(StringReader reader)
{
int character = ReadAndSkipWhitespace(reader);
if (character < 0)
return new OSD(); // server returned an empty file, so we're going to pass along a null LLSD object
OSD osd;
int matching;
switch ((char)character)
{
case undefNotationValue:
osd = new OSD();
break;
case trueNotationValueOne:
osd = OSD.FromBoolean(true);
break;
case trueNotationValueTwo:
matching = BufferCharactersEqual(reader, trueNotationValueTwoFull, 1);
if (matching > 1 && matching < trueNotationValueTwoFull.Length)
throw new OSDException("Notation LLSD parsing: True value parsing error:");
osd = OSD.FromBoolean(true);
break;
case trueNotationValueThree:
matching = BufferCharactersEqual(reader, trueNotationValueThreeFull, 1);
if (matching > 1 && matching < trueNotationValueThreeFull.Length)
throw new OSDException("Notation LLSD parsing: True value parsing error:");
osd = OSD.FromBoolean(true);
break;
case falseNotationValueOne:
osd = OSD.FromBoolean(false);
break;
case falseNotationValueTwo:
matching = BufferCharactersEqual(reader, falseNotationValueTwoFull, 1);
if (matching > 1 && matching < falseNotationValueTwoFull.Length)
throw new OSDException("Notation LLSD parsing: True value parsing error:");
osd = OSD.FromBoolean(false);
break;
case falseNotationValueThree:
matching = BufferCharactersEqual(reader, falseNotationValueThreeFull, 1);
if (matching > 1 && matching < falseNotationValueThreeFull.Length)
throw new OSDException("Notation LLSD parsing: True value parsing error:");
osd = OSD.FromBoolean(false);
break;
case integerNotationMarker:
osd = DeserializeLLSDNotationInteger(reader);
break;
case realNotationMarker:
osd = DeserializeLLSDNotationReal(reader);
break;
case uuidNotationMarker:
char[] uuidBuf = new char[36];
if (reader.Read(uuidBuf, 0, 36) < 36)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in UUID.");
UUID lluuid;
if (!UUID.TryParse(new String(uuidBuf), out lluuid))
throw new OSDException("Notation LLSD parsing: Invalid UUID discovered.");
osd = OSD.FromUUID(lluuid);
break;
case binaryNotationMarker:
byte[] bytes = Utils.EmptyBytes;
int bChar = reader.Peek();
if (bChar < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in binary.");
if ((char)bChar == sizeBeginNotationMarker)
{
throw new OSDException("Notation LLSD parsing: Raw binary encoding not supported.");
}
else if (Char.IsDigit((char)bChar))
{
char[] charsBaseEncoding = new char[2];
if (reader.Read(charsBaseEncoding, 0, 2) < 2)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in binary.");
int baseEncoding;
if (!Int32.TryParse(new String(charsBaseEncoding), out baseEncoding))
throw new OSDException("Notation LLSD parsing: Invalid binary encoding base.");
if (baseEncoding == 64)
{
if (reader.Read() < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in binary.");
string bytes64 = GetStringDelimitedBy(reader, doubleQuotesNotationMarker);
bytes = Convert.FromBase64String(bytes64);
}
else
{
throw new OSDException("Notation LLSD parsing: Encoding base" + baseEncoding + " + not supported.");
}
}
osd = OSD.FromBinary(bytes);
break;
case stringNotationMarker:
int numChars = GetLengthInBrackets(reader);
if (reader.Read() < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in string.");
char[] chars = new char[numChars];
if (reader.Read(chars, 0, numChars) < numChars)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in string.");
if (reader.Read() < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in string.");
osd = OSD.FromString(new String(chars));
break;
case singleQuotesNotationMarker:
string sOne = GetStringDelimitedBy(reader, singleQuotesNotationMarker);
osd = OSD.FromString(sOne);
break;
case doubleQuotesNotationMarker:
string sTwo = GetStringDelimitedBy(reader, doubleQuotesNotationMarker);
osd = OSD.FromString(sTwo);
break;
case uriNotationMarker:
if (reader.Read() < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in string.");
string sUri = GetStringDelimitedBy(reader, doubleQuotesNotationMarker);
Uri uri;
try
{
uri = new Uri(sUri, UriKind.RelativeOrAbsolute);
}
catch
{
throw new OSDException("Notation LLSD parsing: Invalid Uri format detected.");
}
osd = OSD.FromUri(uri);
break;
case dateNotationMarker:
if (reader.Read() < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in date.");
string date = GetStringDelimitedBy(reader, doubleQuotesNotationMarker);
DateTime dt;
if (!DateTime.TryParse(date, out dt))
throw new OSDException("Notation LLSD parsing: Invalid date discovered.");
osd = OSD.FromDate(dt);
break;
case arrayBeginNotationMarker:
osd = DeserializeLLSDNotationArray(reader);
break;
case mapBeginNotationMarker:
osd = DeserializeLLSDNotationMap(reader);
break;
default:
throw new OSDException("Notation LLSD parsing: Unknown type marker '" + (char)character + "'.");
}
return osd;
}
private static OSD DeserializeLLSDNotationInteger(StringReader reader)
{
int character;
StringBuilder s = new StringBuilder();
if (((character = reader.Peek()) > 0) && ((char)character == '-'))
{
s.Append((char)character);
reader.Read();
}
while ((character = reader.Peek()) > 0 &&
Char.IsDigit((char)character))
{
s.Append((char)character);
reader.Read();
}
int integer;
if (!Int32.TryParse(s.ToString(), out integer))
throw new OSDException("Notation LLSD parsing: Can't parse integer value." + s.ToString());
return OSD.FromInteger(integer);
}
private static OSD DeserializeLLSDNotationReal(StringReader reader)
{
int character;
StringBuilder s = new StringBuilder();
if (((character = reader.Peek()) > 0) &&
((char)character == '-' && (char)character == '+'))
{
s.Append((char)character);
reader.Read();
}
while (((character = reader.Peek()) > 0) &&
(Char.IsDigit((char)character) || (char)character == '.' ||
(char)character == 'e' || (char)character == 'E' ||
(char)character == '+' || (char)character == '-'))
{
s.Append((char)character);
reader.Read();
}
double dbl;
if (!Utils.TryParseDouble(s.ToString(), out dbl))
throw new OSDException("Notation LLSD parsing: Can't parse real value: " + s.ToString());
return OSD.FromReal(dbl);
}
private static OSD DeserializeLLSDNotationArray(StringReader reader)
{
int character;
OSDArray osdArray = new OSDArray();
while (((character = PeekAndSkipWhitespace(reader)) > 0) &&
((char)character != arrayEndNotationMarker))
{
osdArray.Add(DeserializeLLSDNotationElement(reader));
character = ReadAndSkipWhitespace(reader);
if (character < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of array discovered.");
else if ((char)character == kommaNotationDelimiter)
continue;
else if ((char)character == arrayEndNotationMarker)
break;
}
if (character < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of array discovered.");
return (OSD)osdArray;
}
private static OSD DeserializeLLSDNotationMap(StringReader reader)
{
int character;
OSDMap osdMap = new OSDMap();
while (((character = PeekAndSkipWhitespace(reader)) > 0) &&
((char)character != mapEndNotationMarker))
{
OSD osdKey = DeserializeLLSDNotationElement(reader);
if (osdKey.Type != OSDType.String)
throw new OSDException("Notation LLSD parsing: Invalid key in map");
string key = osdKey.AsString();
character = ReadAndSkipWhitespace(reader);
if ((char)character != keyNotationDelimiter)
throw new OSDException("Notation LLSD parsing: Unexpected end of stream in map.");
if ((char)character != keyNotationDelimiter)
throw new OSDException("Notation LLSD parsing: Invalid delimiter in map.");
osdMap[key] = DeserializeLLSDNotationElement(reader);
character = ReadAndSkipWhitespace(reader);
if (character < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of map discovered.");
else if ((char)character == kommaNotationDelimiter)
continue;
else if ((char)character == mapEndNotationMarker)
break;
}
if (character < 0)
throw new OSDException("Notation LLSD parsing: Unexpected end of map discovered.");
return (OSD)osdMap;
}
private static void SerializeLLSDNotationElement(StringWriter writer, OSD osd)
{
switch (osd.Type)
{
case OSDType.Unknown:
writer.Write(undefNotationValue);
break;
case OSDType.Boolean:
if (osd.AsBoolean())
writer.Write(trueNotationValueTwo);
else
writer.Write(falseNotationValueTwo);
break;
case OSDType.Integer:
writer.Write(integerNotationMarker);
writer.Write(osd.AsString());
break;
case OSDType.Real:
writer.Write(realNotationMarker);
writer.Write(osd.AsString());
break;
case OSDType.UUID:
writer.Write(uuidNotationMarker);
writer.Write(osd.AsString());
break;
case OSDType.String:
writer.Write(singleQuotesNotationMarker);
writer.Write(EscapeCharacter(osd.AsString(), singleQuotesNotationMarker));
writer.Write(singleQuotesNotationMarker);
break;
case OSDType.Binary:
writer.Write(binaryNotationMarker);
writer.Write("64");
writer.Write(doubleQuotesNotationMarker);
writer.Write(osd.AsString());
writer.Write(doubleQuotesNotationMarker);
break;
case OSDType.Date:
writer.Write(dateNotationMarker);
writer.Write(doubleQuotesNotationMarker);
writer.Write(osd.AsString());
writer.Write(doubleQuotesNotationMarker);
break;
case OSDType.URI:
writer.Write(uriNotationMarker);
writer.Write(doubleQuotesNotationMarker);
writer.Write(EscapeCharacter(osd.AsString(), doubleQuotesNotationMarker));
writer.Write(doubleQuotesNotationMarker);
break;
case OSDType.Array:
SerializeLLSDNotationArray(writer, (OSDArray)osd);
break;
case OSDType.Map:
SerializeLLSDNotationMap(writer, (OSDMap)osd);
break;
default:
throw new OSDException("Notation serialization: Not existing element discovered.");
}
}
private static void SerializeLLSDNotationArray(StringWriter writer, OSDArray osdArray)
{
writer.Write(arrayBeginNotationMarker);
int lastIndex = osdArray.Count - 1;
for (int idx = 0; idx <= lastIndex; idx++)
{
SerializeLLSDNotationElement(writer, osdArray[idx]);
if (idx < lastIndex)
writer.Write(kommaNotationDelimiter);
}
writer.Write(arrayEndNotationMarker);
}
private static void SerializeLLSDNotationMap(StringWriter writer, OSDMap osdMap)
{
writer.Write(mapBeginNotationMarker);
int lastIndex = osdMap.Count - 1;
int idx = 0;
foreach (KeyValuePair<string, OSD> kvp in osdMap)
{
writer.Write(singleQuotesNotationMarker);
writer.Write(EscapeCharacter(kvp.Key, singleQuotesNotationMarker));
writer.Write(singleQuotesNotationMarker);
writer.Write(keyNotationDelimiter);
SerializeLLSDNotationElement(writer, kvp.Value);
if (idx < lastIndex)
writer.Write(kommaNotationDelimiter);
idx++;
}
writer.Write(mapEndNotationMarker);
}
private static void SerializeLLSDNotationElementFormatted(StringWriter writer, string indent, OSD osd)
{
switch (osd.Type)
{
case OSDType.Unknown:
writer.Write(undefNotationValue);
break;
case OSDType.Boolean:
if (osd.AsBoolean())
writer.Write(trueNotationValueTwo);
else
writer.Write(falseNotationValueTwo);
break;
case OSDType.Integer:
writer.Write(integerNotationMarker);
writer.Write(osd.AsString());
break;
case OSDType.Real:
writer.Write(realNotationMarker);
writer.Write(osd.AsString());
break;
case OSDType.UUID:
writer.Write(uuidNotationMarker);
writer.Write(osd.AsString());
break;
case OSDType.String:
writer.Write(singleQuotesNotationMarker);
writer.Write(EscapeCharacter(osd.AsString(), singleQuotesNotationMarker));
writer.Write(singleQuotesNotationMarker);
break;
case OSDType.Binary:
writer.Write(binaryNotationMarker);
writer.Write("64");
writer.Write(doubleQuotesNotationMarker);
writer.Write(osd.AsString());
writer.Write(doubleQuotesNotationMarker);
break;
case OSDType.Date:
writer.Write(dateNotationMarker);
writer.Write(doubleQuotesNotationMarker);
writer.Write(osd.AsString());
writer.Write(doubleQuotesNotationMarker);
break;
case OSDType.URI:
writer.Write(uriNotationMarker);
writer.Write(doubleQuotesNotationMarker);
writer.Write(EscapeCharacter(osd.AsString(), doubleQuotesNotationMarker));
writer.Write(doubleQuotesNotationMarker);
break;
case OSDType.Array:
SerializeLLSDNotationArrayFormatted(writer, indent + baseIndent, (OSDArray)osd);
break;
case OSDType.Map:
SerializeLLSDNotationMapFormatted(writer, indent + baseIndent, (OSDMap)osd);
break;
default:
throw new OSDException("Notation serialization: Not existing element discovered.");
}
}
private static void SerializeLLSDNotationArrayFormatted(StringWriter writer, string intend, OSDArray osdArray)
{
writer.WriteLine();
writer.Write(intend);
writer.Write(arrayBeginNotationMarker);
int lastIndex = osdArray.Count - 1;
for (int idx = 0; idx <= lastIndex; idx++)
{
if (osdArray[idx].Type != OSDType.Array && osdArray[idx].Type != OSDType.Map)
writer.WriteLine();
writer.Write(intend + baseIndent);
SerializeLLSDNotationElementFormatted(writer, intend, osdArray[idx]);
if (idx < lastIndex)
{
writer.Write(kommaNotationDelimiter);
}
}
writer.WriteLine();
writer.Write(intend);
writer.Write(arrayEndNotationMarker);
}
private static void SerializeLLSDNotationMapFormatted(StringWriter writer, string intend, OSDMap osdMap)
{
writer.WriteLine();
writer.Write(intend);
writer.WriteLine(mapBeginNotationMarker);
int lastIndex = osdMap.Count - 1;
int idx = 0;
foreach (KeyValuePair<string, OSD> kvp in osdMap)
{
writer.Write(intend + baseIndent);
writer.Write(singleQuotesNotationMarker);
writer.Write(EscapeCharacter(kvp.Key, singleQuotesNotationMarker));
writer.Write(singleQuotesNotationMarker);
writer.Write(keyNotationDelimiter);
SerializeLLSDNotationElementFormatted(writer, intend, kvp.Value);
if (idx < lastIndex)
{
writer.WriteLine();
writer.Write(intend + baseIndent);
writer.WriteLine(kommaNotationDelimiter);
}
idx++;
}
writer.WriteLine();
writer.Write(intend);
writer.Write(mapEndNotationMarker);
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
public static int PeekAndSkipWhitespace(StringReader reader)
{
int character;
while ((character = reader.Peek()) > 0)
{
char c = (char)character;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
{
reader.Read();
continue;
}
else
break;
}
return character;
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
public static int ReadAndSkipWhitespace(StringReader reader)
{
int character = PeekAndSkipWhitespace(reader);
reader.Read();
return character;
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
public static int GetLengthInBrackets(StringReader reader)
{
int character;
StringBuilder s = new StringBuilder();
if (((character = PeekAndSkipWhitespace(reader)) > 0) &&
((char)character == sizeBeginNotationMarker))
{
reader.Read();
}
while (((character = reader.Read()) > 0) &&
Char.IsDigit((char)character) &&
((char)character != sizeEndNotationMarker))
{
s.Append((char)character);
}
if (character < 0)
throw new OSDException("Notation LLSD parsing: Can't parse length value cause unexpected end of stream.");
int length;
if (!Int32.TryParse(s.ToString(), out length))
throw new OSDException("Notation LLSD parsing: Can't parse length value.");
return length;
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="delimiter"></param>
/// <returns></returns>
public static string GetStringDelimitedBy(StringReader reader, char delimiter)
{
int character;
bool foundEscape = false;
StringBuilder s = new StringBuilder();
while (((character = reader.Read()) > 0) &&
(((char)character != delimiter) ||
((char)character == delimiter && foundEscape)))
{
if (foundEscape)
{
foundEscape = false;
switch ((char)character)
{
case 'a':
s.Append('\a');
break;
case 'b':
s.Append('\b');
break;
case 'f':
s.Append('\f');
break;
case 'n':
s.Append('\n');
break;
case 'r':
s.Append('\r');
break;
case 't':
s.Append('\t');
break;
case 'v':
s.Append('\v');
break;
default:
s.Append((char)character);
break;
}
}
else if ((char)character == '\\')
foundEscape = true;
else
s.Append((char)character);
}
if (character < 0)
throw new OSDException("Notation LLSD parsing: Can't parse text because unexpected end of stream while expecting a '"
+ delimiter + "' character.");
return s.ToString();
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <returns></returns>
public static int BufferCharactersEqual(StringReader reader, char[] buffer, int offset)
{
int character;
int lastIndex = buffer.Length - 1;
int crrIndex = offset;
bool charactersEqual = true;
while ((character = reader.Peek()) > 0 &&
crrIndex <= lastIndex &&
charactersEqual)
{
if (((char)character) != buffer[crrIndex])
{
charactersEqual = false;
break;
}
crrIndex++;
reader.Read();
}
return crrIndex;
}
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <param name="c"></param>
/// <returns></returns>
public static string UnescapeCharacter(String s, char c)
{
string oldOne = "\\" + c;
string newOne = new String(c, 1);
String sOne = s.Replace("\\\\", "\\").Replace(oldOne, newOne);
return sOne;
}
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <param name="c"></param>
/// <returns></returns>
public static string EscapeCharacter(String s, char c)
{
string oldOne = new String(c, 1);
string newOne = "\\" + c;
String sOne = s.Replace("\\", "\\\\").Replace(oldOne, newOne);
return sOne;
}
}
}
@@ -0,0 +1,652 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Xml;
using System.Xml.Schema;
using System.Text;
namespace OpenMetaverse.StructuredData
{
/// <summary>
///
/// </summary>
public static partial class OSDParser
{
private static XmlSchema XmlSchema;
private static XmlTextReader XmlTextReader;
private static string LastXmlErrors = String.Empty;
private static object XmlValidationLock = new object();
/// <summary>
///
/// </summary>
/// <param name="xmlData"></param>
/// <returns></returns>
public static OSD DeserializeLLSDXml(byte[] xmlData)
{
return DeserializeLLSDXml(new XmlTextReader(new MemoryStream(xmlData, false)));
}
public static OSD DeserializeLLSDXml(Stream xmlStream)
{
return DeserializeLLSDXml(new XmlTextReader(xmlStream));
}
/// <summary>
///
/// </summary>
/// <param name="xmlData"></param>
/// <returns></returns>
public static OSD DeserializeLLSDXml(string xmlData)
{
byte[] bytes = Utils.StringToBytes(xmlData);
return DeserializeLLSDXml(new XmlTextReader(new MemoryStream(bytes, false)));
}
/// <summary>
///
/// </summary>
/// <param name="xmlData"></param>
/// <returns></returns>
public static OSD DeserializeLLSDXml(XmlTextReader xmlData)
{
try
{
xmlData.Read();
SkipWhitespace(xmlData);
xmlData.Read();
OSD ret = ParseLLSDXmlElement(xmlData);
return ret;
}
catch
{
return new OSD();
}
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static byte[] SerializeLLSDXmlBytes(OSD data)
{
return Encoding.UTF8.GetBytes(SerializeLLSDXmlString(data));
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string SerializeLLSDXmlString(OSD data)
{
StringWriter sw = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(sw);
writer.Formatting = Formatting.None;
writer.WriteStartElement(String.Empty, "llsd", String.Empty);
SerializeLLSDXmlElement(writer, data);
writer.WriteEndElement();
writer.Close();
return sw.ToString();
}
/// <summary>
///
/// </summary>
/// <param name="writer"></param>
/// <param name="data"></param>
public static void SerializeLLSDXmlElement(XmlTextWriter writer, OSD data)
{
switch (data.Type)
{
case OSDType.Unknown:
writer.WriteStartElement(String.Empty, "undef", String.Empty);
writer.WriteEndElement();
break;
case OSDType.Boolean:
writer.WriteStartElement(String.Empty, "boolean", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.Integer:
writer.WriteStartElement(String.Empty, "integer", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.Real:
writer.WriteStartElement(String.Empty, "real", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.String:
writer.WriteStartElement(String.Empty, "string", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.UUID:
writer.WriteStartElement(String.Empty, "uuid", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.Date:
writer.WriteStartElement(String.Empty, "date", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.URI:
writer.WriteStartElement(String.Empty, "uri", String.Empty);
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.Binary:
writer.WriteStartElement(String.Empty, "binary", String.Empty);
writer.WriteStartAttribute(String.Empty, "encoding", String.Empty);
writer.WriteString("base64");
writer.WriteEndAttribute();
writer.WriteString(data.AsString());
writer.WriteEndElement();
break;
case OSDType.Map:
OSDMap map = (OSDMap)data;
writer.WriteStartElement(String.Empty, "map", String.Empty);
foreach (KeyValuePair<string, OSD> kvp in map)
{
writer.WriteStartElement(String.Empty, "key", String.Empty);
writer.WriteString(kvp.Key);
writer.WriteEndElement();
SerializeLLSDXmlElement(writer, kvp.Value);
}
writer.WriteEndElement();
break;
case OSDType.Array:
OSDArray array = (OSDArray)data;
writer.WriteStartElement(String.Empty, "array", String.Empty);
for (int i = 0; i < array.Count; i++)
{
SerializeLLSDXmlElement(writer, array[i]);
}
writer.WriteEndElement();
break;
}
}
/// <summary>
///
/// </summary>
/// <param name="xmlData"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool TryValidateLLSDXml(XmlTextReader xmlData, out string error)
{
lock (XmlValidationLock)
{
LastXmlErrors = String.Empty;
XmlTextReader = xmlData;
CreateLLSDXmlSchema();
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.ValidationType = ValidationType.Schema;
readerSettings.Schemas.Add(XmlSchema);
readerSettings.ValidationEventHandler += new ValidationEventHandler(LLSDXmlSchemaValidationHandler);
XmlReader reader = XmlReader.Create(xmlData, readerSettings);
try
{
while (reader.Read()) { }
}
catch (XmlException)
{
error = LastXmlErrors;
return false;
}
if (LastXmlErrors == String.Empty)
{
error = null;
return true;
}
else
{
error = LastXmlErrors;
return false;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
private static OSD ParseLLSDXmlElement(XmlTextReader reader)
{
SkipWhitespace(reader);
if (reader.NodeType != XmlNodeType.Element)
throw new OSDException("Expected an element");
string type = reader.LocalName;
OSD ret;
switch (type)
{
case "undef":
if (reader.IsEmptyElement)
{
reader.Read();
return new OSD();
}
reader.Read();
SkipWhitespace(reader);
ret = new OSD();
break;
case "boolean":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromBoolean(false);
}
if (reader.Read())
{
string s = reader.ReadString().Trim();
if (!String.IsNullOrEmpty(s) && (s == "true" || s == "1"))
{
ret = OSD.FromBoolean(true);
break;
}
}
ret = OSD.FromBoolean(false);
break;
case "integer":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromInteger(0);
}
if (reader.Read())
{
int value = 0;
Int32.TryParse(reader.ReadString().Trim(), out value);
ret = OSD.FromInteger(value);
break;
}
ret = OSD.FromInteger(0);
break;
case "real":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromReal(0d);
}
if (reader.Read())
{
double value = 0d;
string str = reader.ReadString().Trim().ToLower();
if (str == "nan")
value = Double.NaN;
else
Utils.TryParseDouble(str, out value);
ret = OSD.FromReal(value);
break;
}
ret = OSD.FromReal(0d);
break;
case "uuid":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromUUID(UUID.Zero);
}
if (reader.Read())
{
UUID value = UUID.Zero;
UUID.TryParse(reader.ReadString().Trim(), out value);
ret = OSD.FromUUID(value);
break;
}
ret = OSD.FromUUID(UUID.Zero);
break;
case "date":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromDate(Utils.Epoch);
}
if (reader.Read())
{
DateTime value = Utils.Epoch;
DateTime.TryParse(reader.ReadString().Trim(), out value);
ret = OSD.FromDate(value);
break;
}
ret = OSD.FromDate(Utils.Epoch);
break;
case "string":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromString(String.Empty);
}
if (reader.Read())
{
ret = OSD.FromString(reader.ReadString());
break;
}
ret = OSD.FromString(String.Empty);
break;
case "binary":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromBinary(Utils.EmptyBytes);
}
if (reader.GetAttribute("encoding") != null && reader.GetAttribute("encoding") != "base64")
throw new OSDException("Unsupported binary encoding: " + reader.GetAttribute("encoding"));
if (reader.Read())
{
try
{
ret = OSD.FromBinary(Convert.FromBase64String(reader.ReadString().Trim()));
break;
}
catch (FormatException ex)
{
throw new OSDException("Binary decoding exception: " + ex.Message);
}
}
ret = OSD.FromBinary(Utils.EmptyBytes);
break;
case "uri":
if (reader.IsEmptyElement)
{
reader.Read();
return OSD.FromUri(new Uri(String.Empty, UriKind.RelativeOrAbsolute));
}
if (reader.Read())
{
ret = OSD.FromUri(new Uri(reader.ReadString(), UriKind.RelativeOrAbsolute));
break;
}
ret = OSD.FromUri(new Uri(String.Empty, UriKind.RelativeOrAbsolute));
break;
case "map":
return ParseLLSDXmlMap(reader);
case "array":
return ParseLLSDXmlArray(reader);
default:
reader.Read();
ret = null;
break;
}
if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != type)
{
throw new OSDException("Expected </" + type + ">");
}
else
{
reader.Read();
return ret;
}
}
private static OSDMap ParseLLSDXmlMap(XmlTextReader reader)
{
if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "map")
throw new NotImplementedException("Expected <map>");
OSDMap map = new OSDMap();
if (reader.IsEmptyElement)
{
reader.Read();
return map;
}
if (reader.Read())
{
while (true)
{
SkipWhitespace(reader);
if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "map")
{
reader.Read();
break;
}
if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "key")
throw new OSDException("Expected <key>");
string key = reader.ReadString();
if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "key")
throw new OSDException("Expected </key>");
if (reader.Read())
map[key] = ParseLLSDXmlElement(reader);
else
throw new OSDException("Failed to parse a value for key " + key);
}
}
return map;
}
private static OSDArray ParseLLSDXmlArray(XmlTextReader reader)
{
if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "array")
throw new OSDException("Expected <array>");
OSDArray array = new OSDArray();
if (reader.IsEmptyElement)
{
reader.Read();
return array;
}
if (reader.Read())
{
while (true)
{
SkipWhitespace(reader);
if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "array")
{
reader.Read();
break;
}
array.Add(ParseLLSDXmlElement(reader));
}
}
return array;
}
private static void SkipWhitespace(XmlTextReader reader)
{
while (
reader.NodeType == XmlNodeType.Comment ||
reader.NodeType == XmlNodeType.Whitespace ||
reader.NodeType == XmlNodeType.SignificantWhitespace ||
reader.NodeType == XmlNodeType.XmlDeclaration)
{
reader.Read();
}
}
private static void CreateLLSDXmlSchema()
{
if (XmlSchema == null)
{
#region XSD
string schemaText = @"
<?xml version=""1.0"" encoding=""utf-8""?>
<xs:schema elementFormDefault=""qualified"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
<xs:import schemaLocation=""xml.xsd"" namespace=""http://www.w3.org/XML/1998/namespace"" />
<xs:element name=""uri"" type=""xs:string"" />
<xs:element name=""uuid"" type=""xs:string"" />
<xs:element name=""KEYDATA"">
<xs:complexType>
<xs:sequence>
<xs:element ref=""key"" />
<xs:element ref=""DATA"" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=""date"" type=""xs:string"" />
<xs:element name=""key"" type=""xs:string"" />
<xs:element name=""boolean"" type=""xs:string"" />
<xs:element name=""undef"">
<xs:complexType>
<xs:sequence>
<xs:element ref=""EMPTY"" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=""map"">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=""0"" maxOccurs=""unbounded"" ref=""KEYDATA"" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=""real"" type=""xs:string"" />
<xs:element name=""ATOMIC"">
<xs:complexType>
<xs:choice>
<xs:element ref=""undef"" />
<xs:element ref=""boolean"" />
<xs:element ref=""integer"" />
<xs:element ref=""real"" />
<xs:element ref=""uuid"" />
<xs:element ref=""string"" />
<xs:element ref=""date"" />
<xs:element ref=""uri"" />
<xs:element ref=""binary"" />
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name=""DATA"">
<xs:complexType>
<xs:choice>
<xs:element ref=""ATOMIC"" />
<xs:element ref=""map"" />
<xs:element ref=""array"" />
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name=""llsd"">
<xs:complexType>
<xs:sequence>
<xs:element ref=""DATA"" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=""binary"">
<xs:complexType>
<xs:simpleContent>
<xs:extension base=""xs:string"">
<xs:attribute default=""base64"" name=""encoding"" type=""xs:string"" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name=""array"">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=""0"" maxOccurs=""unbounded"" ref=""DATA"" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=""integer"" type=""xs:string"" />
<xs:element name=""string"">
<xs:complexType>
<xs:simpleContent>
<xs:extension base=""xs:string"">
<xs:attribute ref=""xml:space"" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>
";
#endregion XSD
MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(schemaText));
XmlSchema = new XmlSchema();
XmlSchema = XmlSchema.Read(stream, new ValidationEventHandler(LLSDXmlSchemaValidationHandler));
}
}
private static void LLSDXmlSchemaValidationHandler(object sender, ValidationEventArgs args)
{
string error = String.Format("Line: {0} - Position: {1} - {2}", XmlTextReader.LineNumber, XmlTextReader.LinePosition,
args.Message);
if (LastXmlErrors == String.Empty)
LastXmlErrors = error;
else
LastXmlErrors += Environment.NewLine + error;
}
}
}
@@ -0,0 +1,85 @@
<?xml version="1.0" ?>
<project name="OpenMetaverse.StructuredData" default="build">
<target name="build">
<echo message="Build Directory is ${build.dir}" />
<mkdir dir="${build.dir}" />
<csc target="library" debug="${build.debug}" unsafe="True" warnaserror="False" define="TRACE" nostdlib="False" main="" doc="${build.dir}/OpenMetaverse.StructuredData.XML" output="${build.dir}/${project::get-name()}.dll">
<resources prefix="OpenMetaverse.StructuredData" dynamicprefix="true" >
</resources>
<sources failonempty="true">
<include name="StructuredData.cs" />
<include name="JSON/IJsonWrapper.cs" />
<include name="JSON/JsonData.cs" />
<include name="JSON/JsonException.cs" />
<include name="JSON/JsonMapper.cs" />
<include name="JSON/JsonReader.cs" />
<include name="JSON/JsonWriter.cs" />
<include name="JSON/Lexer.cs" />
<include name="JSON/OSDJson.cs" />
<include name="JSON/ParserToken.cs" />
<include name="LLSD/BinaryLLSD.cs" />
<include name="LLSD/NotationLLSD.cs" />
<include name="LLSD/XmlLLSD.cs" />
</sources>
<references basedir="${project::get-base-directory()}">
<lib>
<include name="${project::get-base-directory()}" />
<include name="${build.dir}" />
</lib>
<include name="System.dll" />
<include name="System.Xml.dll" />
<include name="${build.dir}/OpenMetaverseTypes.dll" />
</references>
<nowarn>
<warning number="1591" />
<warning number="1574" />
<warning number="0419" />
<warning number="0618" />
</nowarn>
</csc>
</target>
<target name="clean">
<delete dir="${bin.dir}" failonerror="false" />
<delete dir="${obj.dir}" failonerror="false" />
</target>
<target name="doc" description="Creates documentation.">
<property name="doc.target" value="" />
<if test="${platform::is-unix()}">
<property name="doc.target" value="Web" />
</if>
<ndoc failonerror="false" verbose="true">
<assemblies basedir="${project::get-base-directory()}">
<include name="${build.dir}/${project::get-name()}.dll" />
</assemblies>
<summaries basedir="${project::get-base-directory()}">
<include name="${build.dir}/${project::get-name()}.xml"/>
</summaries>
<referencepaths basedir="${project::get-base-directory()}">
<include name="${build.dir}" />
</referencepaths>
<documenters>
<documenter name="MSDN">
<property name="OutputDirectory" value="${build.dir}/doc/${project::get-name()}" />
<property name="OutputTarget" value="${doc.target}" />
<property name="HtmlHelpName" value="${project::get-name()}" />
<property name="IncludeFavorites" value="False" />
<property name="Title" value="${project::get-name()} SDK Documentation" />
<property name="SplitTOCs" value="False" />
<property name="DefaulTOC" value="" />
<property name="ShowVisualBasic" value="True" />
<property name="AutoDocumentConstructors" value="True" />
<property name="ShowMissingSummaries" value="${build.debug}" />
<property name="ShowMissingRemarks" value="${build.debug}" />
<property name="ShowMissingParams" value="${build.debug}" />
<property name="ShowMissingReturns" value="${build.debug}" />
<property name="ShowMissingValues" value="${build.debug}" />
<property name="DocumentInternals" value="False" />
<property name="DocumentPrivates" value="False" />
<property name="DocumentProtected" value="True" />
<property name="DocumentEmptyNamespaces" value="${build.debug}" />
<property name="IncludeAssemblyVersion" value="True" />
</documenter>
</documenters>
</ndoc>
</target>
</project>
@@ -0,0 +1,39 @@
<Project name="OpenMetaverse.StructuredData" description="" standardNamespace="OpenMetaverse.StructuredData" newfilesearch="None" enableviewstate="True" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject">
<Configurations active="Debug">
<Configuration name="Release" ctype="DotNetProjectConfiguration">
<Output directory="./../bin/" assembly="OpenMetaverse.StructuredData" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
<Build debugmode="True" target="Library" />
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="False" optimize="True" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="TRACE" generatexmldocumentation="True" win32Icon="" ctype="CSharpCompilerParameters" />
</Configuration>
<Configuration name="Debug" ctype="DotNetProjectConfiguration">
<Output directory="./../bin/" assembly="OpenMetaverse.StructuredData" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
<Build debugmode="True" target="Library" />
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="True" optimize="False" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="TRACE;DEBUG" generatexmldocumentation="False" win32Icon="" ctype="CSharpCompilerParameters" />
</Configuration>
</Configurations>
<DeploymentInformation target="" script="" strategy="File">
<excludeFiles />
</DeploymentInformation>
<Contents>
<File name="./StructuredData.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/IJsonWrapper.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/JsonData.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/JsonException.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/JsonMapper.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/JsonReader.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/JsonWriter.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/Lexer.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/OSDJson.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./JSON/ParserToken.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./LLSD/BinaryLLSD.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./LLSD/NotationLLSD.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./LLSD/XmlLLSD.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
</Contents>
<References>
<ProjectReference type="Gac" localcopy="False" refto="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<ProjectReference type="Gac" localcopy="False" refto="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverseTypes" />
</References>
</Project>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,80 @@
<?xml version="1.0" ?>
<project name="OpenMetaverse.Utilities" default="build">
<target name="build">
<echo message="Build Directory is ${build.dir}" />
<mkdir dir="${build.dir}" />
<csc target="library" debug="${build.debug}" unsafe="True" warnaserror="False" define="TRACE" nostdlib="False" main="" doc="${build.dir}/OpenMetaverse.Utilities.XML" output="${build.dir}/${project::get-name()}.dll">
<resources prefix="OpenMetaverse.Utilities" dynamicprefix="true" >
</resources>
<sources failonempty="true">
<include name="RegistrationApi.cs" />
<include name="Utilities.cs" />
<include name="VoiceManager.cs" />
<include name="VoiceManagerBlocking.cs" />
<include name="Properties/AssemblyInfo.cs" />
</sources>
<references basedir="${project::get-base-directory()}">
<lib>
<include name="${project::get-base-directory()}" />
<include name="${build.dir}" />
</lib>
<include name="System.dll" />
<include name="System.Xml.dll" />
<include name="System.Data.dll" />
<include name="${build.dir}/OpenMetaverse.dll" />
<include name="${build.dir}/OpenMetaverseTypes.dll" />
<include name="${build.dir}/OpenMetaverse.StructuredData.dll" />
</references>
<nowarn>
<warning number="1591" />
<warning number="1574" />
<warning number="0419" />
<warning number="0618" />
</nowarn>
</csc>
</target>
<target name="clean">
<delete dir="${bin.dir}" failonerror="false" />
<delete dir="${obj.dir}" failonerror="false" />
</target>
<target name="doc" description="Creates documentation.">
<property name="doc.target" value="" />
<if test="${platform::is-unix()}">
<property name="doc.target" value="Web" />
</if>
<ndoc failonerror="false" verbose="true">
<assemblies basedir="${project::get-base-directory()}">
<include name="${build.dir}/${project::get-name()}.dll" />
</assemblies>
<summaries basedir="${project::get-base-directory()}">
<include name="${build.dir}/${project::get-name()}.xml"/>
</summaries>
<referencepaths basedir="${project::get-base-directory()}">
<include name="${build.dir}" />
</referencepaths>
<documenters>
<documenter name="MSDN">
<property name="OutputDirectory" value="${build.dir}/doc/${project::get-name()}" />
<property name="OutputTarget" value="${doc.target}" />
<property name="HtmlHelpName" value="${project::get-name()}" />
<property name="IncludeFavorites" value="False" />
<property name="Title" value="${project::get-name()} SDK Documentation" />
<property name="SplitTOCs" value="False" />
<property name="DefaulTOC" value="" />
<property name="ShowVisualBasic" value="True" />
<property name="AutoDocumentConstructors" value="True" />
<property name="ShowMissingSummaries" value="${build.debug}" />
<property name="ShowMissingRemarks" value="${build.debug}" />
<property name="ShowMissingParams" value="${build.debug}" />
<property name="ShowMissingReturns" value="${build.debug}" />
<property name="ShowMissingValues" value="${build.debug}" />
<property name="DocumentInternals" value="False" />
<property name="DocumentPrivates" value="False" />
<property name="DocumentProtected" value="True" />
<property name="DocumentEmptyNamespaces" value="${build.debug}" />
<property name="IncludeAssemblyVersion" value="True" />
</documenter>
</documenters>
</ndoc>
</target>
</project>
@@ -0,0 +1,34 @@
<Project name="OpenMetaverse.Utilities" description="" standardNamespace="OpenMetaverse.Utilities" newfilesearch="None" enableviewstate="True" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject">
<Configurations active="Debug">
<Configuration name="Release" ctype="DotNetProjectConfiguration">
<Output directory="./../bin/" assembly="OpenMetaverse.Utilities" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
<Build debugmode="True" target="Library" />
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="False" optimize="True" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="TRACE" generatexmldocumentation="True" win32Icon="" ctype="CSharpCompilerParameters" />
</Configuration>
<Configuration name="Debug" ctype="DotNetProjectConfiguration">
<Output directory="./../bin/" assembly="OpenMetaverse.Utilities" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
<Build debugmode="True" target="Library" />
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="True" optimize="False" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="TRACE;DEBUG" generatexmldocumentation="False" win32Icon="" ctype="CSharpCompilerParameters" />
</Configuration>
</Configurations>
<DeploymentInformation target="" script="" strategy="File">
<excludeFiles />
</DeploymentInformation>
<Contents>
<File name="./RegistrationApi.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./Utilities.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./VoiceManager.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./VoiceManagerBlocking.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
<File name="./Properties/AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
</Contents>
<References>
<ProjectReference type="Gac" localcopy="False" refto="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<ProjectReference type="Gac" localcopy="False" refto="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<ProjectReference type="Gac" localcopy="False" refto="System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverse" />
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverseTypes" />
<ProjectReference type="Project" localcopy="False" refto="OpenMetaverse.StructuredData" />
</References>
</Project>
@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenMetaverse.Utilities")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenMetaverse.Utilities")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b77aa07a-daa3-431f-b5aa-0c9c5e2151fd")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+355
View File
@@ -0,0 +1,355 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 System.Text;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Http;
namespace OpenMetaverse
{
public class RegistrationApi
{
const int REQUEST_TIMEOUT = 1000 * 100;
private struct UserInfo
{
public string FirstName;
public string LastName;
public string Password;
}
private struct RegistrationCaps
{
public Uri CreateUser;
public Uri CheckName;
public Uri GetLastNames;
public Uri GetErrorCodes;
}
public struct LastName
{
public int ID;
public string Name;
}
/// <summary>
/// See https://secure-web6.secondlife.com/developers/third_party_reg/#service_create_user or
/// https://wiki.secondlife.com/wiki/RegAPIDoc for description
/// </summary>
public class CreateUserParam
{
public string FirstName;
public LastName LastName;
public string Email;
public string Password;
public DateTime Birthdate;
// optional:
public Nullable<int> LimitedToEstate;
public string StartRegionName;
public Nullable<Vector3> StartLocation;
public Nullable<Vector3> StartLookAt;
}
private UserInfo _userInfo;
private RegistrationCaps _caps;
private int _initializing;
private List<LastName> _lastNames = new List<LastName>();
private Dictionary<int, string> _errors = new Dictionary<int, string>();
public bool Initializing
{
get
{
System.Diagnostics.Debug.Assert(_initializing <= 0);
return (_initializing < 0);
}
}
public List<LastName> LastNames
{
get
{
lock (_lastNames)
{
if (_lastNames.Count <= 0)
GatherLastNames();
}
return _lastNames;
}
}
public RegistrationApi(string firstName, string lastName, string password)
{
_initializing = -2;
_userInfo = new UserInfo();
_userInfo.FirstName = firstName;
_userInfo.LastName = lastName;
_userInfo.Password = password;
GatherCaps();
}
public void WaitForInitialization()
{
while (Initializing)
System.Threading.Thread.Sleep(10);
}
public Uri RegistrationApiCaps
{
get { return new Uri("https://cap.secondlife.com/get_reg_capabilities"); }
}
private void GatherCaps()
{
// build post data
byte[] postData = Encoding.ASCII.GetBytes(
String.Format("first_name={0}&last_name={1}&password={2}", _userInfo.FirstName, _userInfo.LastName,
_userInfo.Password));
CapsClient request = new CapsClient(RegistrationApiCaps);
request.OnComplete += new CapsClient.CompleteCallback(GatherCapsResponse);
request.BeginGetResponse(postData, "application/x-www-form-urlencoded", REQUEST_TIMEOUT);
}
private void GatherCapsResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
OSDMap respTable = (OSDMap)response;
// parse
_caps = new RegistrationCaps();
_caps.CreateUser = respTable["create_user"].AsUri();
_caps.CheckName = respTable["check_name"].AsUri();
_caps.GetLastNames = respTable["get_last_names"].AsUri();
_caps.GetErrorCodes = respTable["get_error_codes"].AsUri();
// finalize
_initializing++;
GatherErrorMessages();
}
}
private void GatherErrorMessages()
{
if (_caps.GetErrorCodes == null)
throw new InvalidOperationException("access denied"); // this should work even for not-approved users
CapsClient request = new CapsClient(_caps.GetErrorCodes);
request.OnComplete += new CapsClient.CompleteCallback(GatherErrorMessagesResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
}
private void GatherErrorMessagesResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
// parse
//FIXME: wtf?
//foreach (KeyValuePair<string, object> error in (Dictionary<string, object>)response)
//{
//StringBuilder sb = new StringBuilder();
//sb.Append(error[1]);
//sb.Append(" (");
//sb.Append(error[0]);
//sb.Append("): ");
//sb.Append(error[2]);
//_errors.Add((int)error[0], sb.ToString());
//}
// finalize
_initializing++;
}
}
public void GatherLastNames()
{
if (Initializing)
throw new InvalidOperationException("still initializing");
if (_caps.GetLastNames == null)
throw new InvalidOperationException("access denied: only approved developers have access to the registration api");
CapsClient request = new CapsClient(_caps.GetLastNames);
request.OnComplete += new CapsClient.CompleteCallback(GatherLastNamesResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME: Block
}
private void GatherLastNamesResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
//LLSDMap respTable = (LLSDMap)response;
//FIXME:
//_lastNames = new List<LastName>(respTable.Count);
//for (Dictionary<string, object>.Enumerator it = respTable.GetEnumerator(); it.MoveNext(); )
//{
// LastName ln = new LastName();
// ln.ID = int.Parse(it.Current.Key.ToString());
// ln.Name = it.Current.Value.ToString();
// _lastNames.Add(ln);
//}
//_lastNames.Sort(new Comparison<LastName>(delegate(LastName a, LastName b) { return a.Name.CompareTo(b.Name); }));
}
}
public bool CheckName(string firstName, LastName lastName)
{
if (Initializing)
throw new InvalidOperationException("still initializing");
if (_caps.CheckName == null)
throw new InvalidOperationException("access denied; only approved developers have access to the registration api");
// Create the POST data
OSDMap query = new OSDMap();
query.Add("username", OSD.FromString(firstName));
query.Add("last_name_id", OSD.FromInteger(lastName.ID));
//byte[] postData = OSDParser.SerializeXmlBytes(query);
CapsClient request = new CapsClient(_caps.CheckName);
request.OnComplete += new CapsClient.CompleteCallback(CheckNameResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME:
return false;
}
private void CheckNameResponse(CapsClient client, OSD response, Exception error)
{
if (response.Type == OSDType.Boolean)
{
// FIXME:
//(bool)response;
}
else
{
// FIXME:
}
}
/// <summary>
/// Returns the new user ID or throws an exception containing the error code
/// The error codes can be found here: https://wiki.secondlife.com/wiki/RegAPIError
/// </summary>
/// <param name="user">New user account to create</param>
/// <returns>The UUID of the new user account</returns>
public UUID CreateUser(CreateUserParam user)
{
if (Initializing)
throw new InvalidOperationException("still initializing");
if (_caps.CreateUser == null)
throw new InvalidOperationException("access denied; only approved developers have access to the registration api");
// Create the POST data
OSDMap query = new OSDMap();
query.Add("username", OSD.FromString(user.FirstName));
query.Add("last_name_id", OSD.FromInteger(user.LastName.ID));
query.Add("email", OSD.FromString(user.Email));
query.Add("password", OSD.FromString(user.Password));
query.Add("dob", OSD.FromString(user.Birthdate.ToString("yyyy-MM-dd")));
if (user.LimitedToEstate != null)
query.Add("limited_to_estate", OSD.FromInteger(user.LimitedToEstate.Value));
if (!string.IsNullOrEmpty(user.StartRegionName))
query.Add("start_region_name", OSD.FromInteger(user.LimitedToEstate.Value));
if (user.StartLocation != null)
{
query.Add("start_local_x", OSD.FromReal(user.StartLocation.Value.X));
query.Add("start_local_y", OSD.FromReal(user.StartLocation.Value.Y));
query.Add("start_local_z", OSD.FromReal(user.StartLocation.Value.Z));
}
if (user.StartLookAt != null)
{
query.Add("start_look_at_x", OSD.FromReal(user.StartLookAt.Value.X));
query.Add("start_look_at_y", OSD.FromReal(user.StartLookAt.Value.Y));
query.Add("start_look_at_z", OSD.FromReal(user.StartLookAt.Value.Z));
}
//byte[] postData = OSDParser.SerializeXmlBytes(query);
// Make the request
CapsClient request = new CapsClient(_caps.CreateUser);
request.OnComplete += new CapsClient.CompleteCallback(CreateUserResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME: Block
return UUID.Zero;
}
private void CreateUserResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
// everything is okay
// FIXME:
//return new UUID(((Dictionary<string, object>)response)["agent_id"].ToString());
}
else
{
// an error happened
OSDArray al = (OSDArray)response;
StringBuilder sb = new StringBuilder();
foreach (OSD ec in al)
{
if (sb.Length > 0)
sb.Append("; ");
sb.Append(_errors[ec.AsInteger()]);
}
// FIXME:
//throw new Exception("failed to create user: " + sb.ToString());
}
}
}
}
+269
View File
@@ -0,0 +1,269 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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;
using System.Collections.Generic;
using System.Threading;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenMetaverse.Utilities
{
/// <summary>
///
/// </summary>
public enum WaterType
{
/// <summary></summary>
Unknown,
/// <summary></summary>
Dry,
/// <summary></summary>
Waterfront,
/// <summary></summary>
Underwater
}
public static class Realism
{
/// <summary>
/// Aims at the specified position, enters mouselook, presses and
/// releases the left mouse button, and leaves mouselook
/// </summary>
/// <param name="client"></param>
/// <param name="target">Target to shoot at</param>
/// <returns></returns>
public static bool Shoot(GridClient client, Vector3 target)
{
if (client.Self.Movement.TurnToward(target))
return Shoot(client);
else
return false;
}
/// <summary>
/// Enters mouselook, presses and releases the left mouse button, and leaves mouselook
/// </summary>
/// <returns></returns>
public static bool Shoot(GridClient client)
{
if (client.Settings.SEND_AGENT_UPDATES)
{
client.Self.Movement.Mouselook = true;
client.Self.Movement.MLButtonDown = true;
client.Self.Movement.SendUpdate();
client.Self.Movement.MLButtonUp = true;
client.Self.Movement.MLButtonDown = false;
client.Self.Movement.FinishAnim = true;
client.Self.Movement.SendUpdate();
client.Self.Movement.Mouselook = false;
client.Self.Movement.MLButtonUp = false;
client.Self.Movement.FinishAnim = false;
client.Self.Movement.SendUpdate();
return true;
}
else
{
Logger.Log("Attempted Shoot but agent updates are disabled", Helpers.LogLevel.Warning, client);
return false;
}
}
/// <summary>
/// A psuedo-realistic chat function that uses the typing sound and
/// animation, types at three characters per second, and randomly
/// pauses. This function will block until the message has been sent
/// </summary>
/// <param name="client">A reference to the client that will chat</param>
/// <param name="message">The chat message to send</param>
public static void Chat(GridClient client, string message)
{
Chat(client, message, ChatType.Normal, 3);
}
/// <summary>
/// A psuedo-realistic chat function that uses the typing sound and
/// animation, types at a given rate, and randomly pauses. This
/// function will block until the message has been sent
/// </summary>
/// <param name="client">A reference to the client that will chat</param>
/// <param name="message">The chat message to send</param>
/// <param name="type">The chat type (usually Normal, Whisper or Shout)</param>
/// <param name="cps">Characters per second rate for chatting</param>
public static void Chat(GridClient client, string message, ChatType type, int cps)
{
Random rand = new Random();
int characters = 0;
bool typing = true;
// Start typing
client.Self.Chat(String.Empty, 0, ChatType.StartTyping);
client.Self.AnimationStart(Animations.TYPE, false);
while (characters < message.Length)
{
if (!typing)
{
// Start typing again
client.Self.Chat(String.Empty, 0, ChatType.StartTyping);
client.Self.AnimationStart(Animations.TYPE, false);
typing = true;
}
else
{
// Randomly pause typing
if (rand.Next(10) >= 9)
{
client.Self.Chat(String.Empty, 0, ChatType.StopTyping);
client.Self.AnimationStop(Animations.TYPE, false);
typing = false;
}
}
// Sleep for a second and increase the amount of characters we've typed
System.Threading.Thread.Sleep(1000);
characters += cps;
}
// Send the message
client.Self.Chat(message, 0, type);
// Stop typing
client.Self.Chat(String.Empty, 0, ChatType.StopTyping);
client.Self.AnimationStop(Animations.TYPE, false);
}
}
public class ConnectionManager
{
private GridClient Client;
private ulong SimHandle;
private Vector3 Position = Vector3.Zero;
private System.Timers.Timer CheckTimer;
public ConnectionManager(GridClient client, int timerFrequency)
{
Client = client;
CheckTimer = new System.Timers.Timer(timerFrequency);
CheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(CheckTimer_Elapsed);
}
public static bool PersistentLogin(GridClient client, string firstName, string lastName, string password,
string userAgent, string start, string author)
{
int unknownLogins = 0;
Start:
if (client.Network.Login(firstName, lastName, password, userAgent, start, author))
{
Logger.Log("Logged in to " + client.Network.CurrentSim, Helpers.LogLevel.Info, client);
return true;
}
else
{
if (client.Network.LoginErrorKey == "god")
{
Logger.Log("Grid is down, waiting 10 minutes", Helpers.LogLevel.Warning, client);
LoginWait(10);
goto Start;
}
else if (client.Network.LoginErrorKey == "key")
{
Logger.Log("Bad username or password, giving up on login", Helpers.LogLevel.Error, client);
return false;
}
else if (client.Network.LoginErrorKey == "presence")
{
Logger.Log("Server is still logging us out, waiting 1 minute", Helpers.LogLevel.Warning, client);
LoginWait(1);
goto Start;
}
else if (client.Network.LoginErrorKey == "disabled")
{
Logger.Log("This account has been banned! Giving up on login", Helpers.LogLevel.Error, client);
return false;
}
else if (client.Network.LoginErrorKey == "timed out" ||client.Network.LoginErrorKey == "no connection" )
{
Logger.Log("Login request timed out, waiting 1 minute", Helpers.LogLevel.Warning, client);
LoginWait(1);
goto Start;
} else if (client.Network.LoginErrorKey == "bad response") {
Logger.Log("Login server returned unparsable result", Helpers.LogLevel.Warning, client);
LoginWait(1);
goto Start;
} else
{
++unknownLogins;
if (unknownLogins < 5)
{
Logger.Log("Unknown login error, waiting 2 minutes: " + client.Network.LoginErrorKey,
Helpers.LogLevel.Warning, client);
LoginWait(2);
goto Start;
}
else
{
Logger.Log("Too many unknown login error codes, giving up", Helpers.LogLevel.Error, client);
return false;
}
}
}
}
public void StayInSim(ulong handle, Vector3 desiredPosition)
{
SimHandle = handle;
Position = desiredPosition;
CheckTimer.Start();
}
private static void LoginWait(int minutes)
{
Thread.Sleep(1000 * 60 * minutes);
}
private void CheckTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (SimHandle != 0)
{
if (Client.Network.CurrentSim.Handle != 0 &&
Client.Network.CurrentSim.Handle != SimHandle)
{
// Attempt to move to our target sim
Client.Self.Teleport(SimHandle, Position);
}
}
}
}
}
+827
View File
@@ -0,0 +1,827 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net.Sockets;
using System.Text;
using System.IO;
using System.Xml;
using System.Threading;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Http;
using OpenMetaverse.Interfaces;
using OpenMetaverse.Messages.Linden;
namespace OpenMetaverse.Utilities
{
public enum VoiceStatus
{
StatusLoginRetry,
StatusLoggedIn,
StatusJoining,
StatusJoined,
StatusLeftChannel,
BeginErrorStatus,
ErrorChannelFull,
ErrorChannelLocked,
ErrorNotAvailable,
ErrorUnknown
}
public enum VoiceServiceType
{
/// <summary>Unknown voice service level</summary>
Unknown,
/// <summary>Spatialized local chat</summary>
TypeA,
/// <summary>Remote multi-party chat</summary>
TypeB,
/// <summary>One-to-one and small group chat</summary>
TypeC
}
public partial class VoiceManager
{
public const int VOICE_MAJOR_VERSION = 1;
public const string DAEMON_ARGS = " -p tcp -h -c -ll ";
public const int DAEMON_LOG_LEVEL = 1;
public const int DAEMON_PORT = 44124;
public const string VOICE_RELEASE_SERVER = "bhr.vivox.com";
public const string VOICE_DEBUG_SERVER = "bhd.vivox.com";
public const string REQUEST_TERMINATOR = "\n\n\n";
public delegate void LoginStateChangeCallback(int cookie, string accountHandle, int statusCode, string statusString, int state);
public delegate void NewSessionCallback(int cookie, string accountHandle, string eventSessionHandle, int state, string nameString, string uriString);
public delegate void SessionStateChangeCallback(int cookie, string uriString, int statusCode, string statusString, string eventSessionHandle, int state, bool isChannel, string nameString);
public delegate void ParticipantStateChangeCallback(int cookie, string uriString, int statusCode, string statusString, int state, string nameString, string displayNameString, int participantType);
public delegate void ParticipantPropertiesCallback(int cookie, string uriString, int statusCode, string statusString, bool isLocallyMuted, bool isModeratorMuted, bool isSpeaking, int volume, float energy);
public delegate void AuxAudioPropertiesCallback(int cookie, float energy);
public delegate void BasicActionCallback(int cookie, int statusCode, string statusString);
public delegate void ConnectorCreatedCallback(int cookie, int statusCode, string statusString, string connectorHandle);
public delegate void LoginCallback(int cookie, int statusCode, string statusString, string accountHandle);
public delegate void SessionCreatedCallback(int cookie, int statusCode, string statusString, string sessionHandle);
public delegate void DevicesCallback(int cookie, int statusCode, string statusString, string currentDevice);
public delegate void ProvisionAccountCallback(string username, string password);
public delegate void ParcelVoiceInfoCallback(string regionName, int localID, string channelURI);
public event LoginStateChangeCallback OnLoginStateChange;
public event NewSessionCallback OnNewSession;
public event SessionStateChangeCallback OnSessionStateChange;
public event ParticipantStateChangeCallback OnParticipantStateChange;
public event ParticipantPropertiesCallback OnParticipantProperties;
public event AuxAudioPropertiesCallback OnAuxAudioProperties;
public event ConnectorCreatedCallback OnConnectorCreated;
public event LoginCallback OnLogin;
public event SessionCreatedCallback OnSessionCreated;
public event BasicActionCallback OnSessionConnected;
public event BasicActionCallback OnAccountLogout;
public event BasicActionCallback OnConnectorInitiateShutdown;
public event BasicActionCallback OnAccountChannelGetList;
public event BasicActionCallback OnSessionTerminated;
public event DevicesCallback OnCaptureDevices;
public event DevicesCallback OnRenderDevices;
public event ProvisionAccountCallback OnProvisionAccount;
public event ParcelVoiceInfoCallback OnParcelVoiceInfo;
public GridClient Client;
public string VoiceServer = VOICE_RELEASE_SERVER;
public bool Enabled;
protected Voice.TCPPipe _DaemonPipe;
protected VoiceStatus _Status;
protected int _CommandCookie = 0;
protected string _TuningSoundFile = String.Empty;
protected Dictionary<string, string> _ChannelMap = new Dictionary<string, string>();
protected List<string> _CaptureDevices = new List<string>();
protected List<string> _RenderDevices = new List<string>();
#region Response Processing Variables
private bool isEvent = false;
private bool isChannel = false;
private bool isLocallyMuted = false;
private bool isModeratorMuted = false;
private bool isSpeaking = false;
private int cookie = 0;
//private int returnCode = 0;
private int statusCode = 0;
private int volume = 0;
private int state = 0;
private int participantType = 0;
private float energy = 0f;
private string statusString = String.Empty;
//private string uuidString = String.Empty;
private string actionString = String.Empty;
private string connectorHandle = String.Empty;
private string accountHandle = String.Empty;
private string sessionHandle = String.Empty;
private string eventSessionHandle = String.Empty;
private string eventTypeString = String.Empty;
private string uriString = String.Empty;
private string nameString = String.Empty;
//private string audioMediaString = String.Empty;
private string displayNameString = String.Empty;
#endregion Response Processing Variables
public VoiceManager(GridClient client)
{
Client = client;
Client.Network.RegisterEventCallback("RequiredVoiceVersion", new Caps.EventQueueCallback(RequiredVoiceVersionEventHandler));
// Register callback handlers for the blocking functions
RegisterCallbacks();
Enabled = true;
}
public bool IsDaemonRunning()
{
throw new NotImplementedException();
}
public bool StartDaemon()
{
throw new NotImplementedException();
}
public void StopDaemon()
{
throw new NotImplementedException();
}
public bool ConnectToDaemon()
{
if (!Enabled) return false;
return ConnectToDaemon("127.0.0.1", DAEMON_PORT);
}
public bool ConnectToDaemon(string address, int port)
{
if (!Enabled) return false;
_DaemonPipe = new Voice.TCPPipe();
_DaemonPipe.OnDisconnected += new Voice.TCPPipe.OnDisconnectedCallback(_DaemonPipe_OnDisconnected);
_DaemonPipe.OnReceiveLine += new Voice.TCPPipe.OnReceiveLineCallback(_DaemonPipe_OnReceiveLine);
SocketException se = _DaemonPipe.Connect(address, port);
if (se == null)
{
return true;
}
else
{
Console.WriteLine("Connection failed: " + se.Message);
return false;
}
}
public Dictionary<string, string> GetChannelMap()
{
return new Dictionary<string, string>(_ChannelMap);
}
public List<string> CurrentCaptureDevices()
{
return new List<string>(_CaptureDevices);
}
public List<string> CurrentRenderDevices()
{
return new List<string>(_RenderDevices);
}
public string VoiceAccountFromUUID(UUID id)
{
string result = "x" + Convert.ToBase64String(id.GetBytes());
return result.Replace('+', '-').Replace('/', '_');
}
public UUID UUIDFromVoiceAccount(string accountName)
{
if (accountName.Length == 25 && accountName[0] == 'x' && accountName[23] == '=' && accountName[24] == '=')
{
accountName = accountName.Replace('/', '_').Replace('+', '-');
byte[] idBytes = Convert.FromBase64String(accountName);
if (idBytes.Length == 16)
return new UUID(idBytes, 0);
else
return UUID.Zero;
}
else
{
return UUID.Zero;
}
}
public string SIPURIFromVoiceAccount(string account)
{
return String.Format("sip:{0}@{1}", account, VoiceServer);
}
public int RequestCaptureDevices()
{
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.GetCaptureDevices.1\"></Request>{1}",
_CommandCookie++,
REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestCaptureDevices() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestRenderDevices()
{
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.GetRenderDevices.1\"></Request>{1}",
_CommandCookie++,
REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestRenderDevices() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestCreateConnector()
{
return RequestCreateConnector(VoiceServer);
}
public int RequestCreateConnector(string voiceServer)
{
if (_DaemonPipe.Connected)
{
VoiceServer = voiceServer;
string accountServer = String.Format("https://www.{0}/api2/", VoiceServer);
string logPath = ".";
StringBuilder request = new StringBuilder();
request.Append(String.Format("<Request requestId=\"{0}\" action=\"Connector.Create.1\">", _CommandCookie++));
request.Append("<ClientName>V2 SDK</ClientName>");
request.Append(String.Format("<AccountManagementServer>{0}</AccountManagementServer>", accountServer));
request.Append("<Logging>");
request.Append("<Enabled>false</Enabled>");
request.Append(String.Format("<Folder>{0}</Folder>", logPath));
request.Append("<FileNamePrefix>vivox-gateway</FileNamePrefix>");
request.Append("<FileNameSuffix>.log</FileNameSuffix>");
request.Append("<LogLevel>0</LogLevel>");
request.Append("</Logging>");
request.Append("</Request>");
request.Append(REQUEST_TERMINATOR);
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(request.ToString()));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.CreateConnector() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
private bool RequestVoiceInternal(string me, CapsClient.CompleteCallback callback, string capsName)
{
if (Enabled && Client.Network.Connected)
{
if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null)
{
Uri url = Client.Network.CurrentSim.Caps.CapabilityURI(capsName);
if (url != null)
{
CapsClient request = new CapsClient(url);
OSDMap body = new OSDMap();
request.OnComplete += new CapsClient.CompleteCallback(callback);
request.BeginGetResponse(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
return true;
}
else
{
Logger.Log("VoiceManager." + me + "(): " + capsName + " capability is missing",
Helpers.LogLevel.Info, Client);
return false;
}
}
}
Logger.Log("VoiceManager.RequestVoiceInternal(): Voice system is currently disabled",
Helpers.LogLevel.Info, Client);
return false;
}
public bool RequestProvisionAccount()
{
return RequestVoiceInternal("RequestProvisionAccount", ProvisionCapsResponse, "ProvisionVoiceAccountRequest");
}
public bool RequestParcelVoiceInfo()
{
return RequestVoiceInternal("RequestParcelVoiceInfo", ParcelVoiceInfoResponse, "ParcelVoiceInfoRequest");
}
public int RequestLogin(string accountName, string password, string connectorHandle)
{
if (_DaemonPipe.Connected)
{
StringBuilder request = new StringBuilder();
request.Append(String.Format("<Request requestId=\"{0}\" action=\"Account.Login.1\">", _CommandCookie++));
request.Append(String.Format("<ConnectorHandle>{0}</ConnectorHandle>", connectorHandle));
request.Append(String.Format("<AccountName>{0}</AccountName>", accountName));
request.Append(String.Format("<AccountPassword>{0}</AccountPassword>", password));
request.Append("<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>");
request.Append("<AccountURI />");
request.Append("<ParticipantPropertyFrequency>10</ParticipantPropertyFrequency>");
request.Append("<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>");
request.Append("</Request>");
request.Append(REQUEST_TERMINATOR);
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(request.ToString()));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.Login() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestSetRenderDevice(string deviceName)
{
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.SetRenderDevice.1\"><RenderDeviceSpecifier>{1}</RenderDeviceSpecifier></Request>{2}",
_CommandCookie, deviceName, REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestSetRenderDevice() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestStartTuningMode(int duration)
{
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.CaptureAudioStart.1\"><Duration>{1}</Duration></Request>{2}",
_CommandCookie, duration, REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestStartTuningMode() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestStopTuningMode()
{
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.CaptureAudioStop.1\"></Request>{1}",
_CommandCookie, REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestStopTuningMode() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return _CommandCookie - 1;
}
}
public int RequestSetSpeakerVolume(int volume)
{
if (volume < 0 || volume > 100)
throw new ArgumentException("volume must be between 0 and 100", "volume");
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.SetSpeakerLevel.1\"><Level>{1}</Level></Request>{2}",
_CommandCookie, volume, REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestSetSpeakerVolume() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestSetCaptureVolume(int volume)
{
if (volume < 0 || volume > 100)
throw new ArgumentException("volume must be between 0 and 100", "volume");
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.SetMicLevel.1\"><Level>{1}</Level></Request>{2}",
_CommandCookie, volume, REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestSetCaptureVolume() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
/// <summary>
/// Does not appear to be working
/// </summary>
/// <param name="fileName"></param>
/// <param name="loop"></param>
public int RequestRenderAudioStart(string fileName, bool loop)
{
if (_DaemonPipe.Connected)
{
_TuningSoundFile = fileName;
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.RenderAudioStart.1\"><SoundFilePath>{1}</SoundFilePath><Loop>{2}</Loop></Request>{3}",
_CommandCookie++, _TuningSoundFile, (loop ? "1" : "0"), REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestRenderAudioStart() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
public int RequestRenderAudioStop()
{
if (_DaemonPipe.Connected)
{
_DaemonPipe.SendData(Encoding.ASCII.GetBytes(String.Format(
"<Request requestId=\"{0}\" action=\"Aux.RenderAudioStop.1\"><SoundFilePath>{1}</SoundFilePath></Request>{2}",
_CommandCookie++, _TuningSoundFile, REQUEST_TERMINATOR)));
return _CommandCookie - 1;
}
else
{
Logger.Log("VoiceManager.RequestRenderAudioStop() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
return -1;
}
}
#region Callbacks
private void RequiredVoiceVersionEventHandler(string capsKey, IMessage message, Simulator simulator)
{
RequiredVoiceVersionMessage msg = (RequiredVoiceVersionMessage)message;
if (VOICE_MAJOR_VERSION != msg.MajorVersion)
{
Logger.Log(String.Format("Voice version mismatch! Got {0}, expecting {1}. Disabling the voice manager",
msg.MajorVersion, VOICE_MAJOR_VERSION), Helpers.LogLevel.Error, Client);
Enabled = false;
}
else
{
Logger.DebugLog("Voice version " + msg.MajorVersion + " verified", Client);
}
}
private void ProvisionCapsResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
OSDMap respTable = (OSDMap)response;
if (OnProvisionAccount != null)
{
try { OnProvisionAccount(respTable["username"].AsString(), respTable["password"].AsString()); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
}
private void ParcelVoiceInfoResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
OSDMap respTable = (OSDMap)response;
string regionName = respTable["region_name"].AsString();
int localID = (int)respTable["parcel_local_id"].AsInteger();
string channelURI = null;
if (respTable["voice_credentials"] is OSDMap)
{
OSDMap creds = (OSDMap)respTable["voice_credentials"];
channelURI = creds["channel_uri"].AsString();
}
if (OnParcelVoiceInfo != null) OnParcelVoiceInfo(regionName, localID, channelURI);
}
}
private void _DaemonPipe_OnDisconnected(SocketException se)
{
if (se != null) Console.WriteLine("Disconnected! " + se.Message);
else Console.WriteLine("Disconnected!");
}
private void _DaemonPipe_OnReceiveLine(string line)
{
XmlTextReader reader = new XmlTextReader(new StringReader(line));
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
if (reader.Depth == 0)
{
isEvent = (reader.Name == "Event");
if (isEvent || reader.Name == "Response")
{
for (int i = 0; i < reader.AttributeCount; i++)
{
reader.MoveToAttribute(i);
switch (reader.Name)
{
// case "requestId":
// uuidString = reader.Value;
// break;
case "action":
actionString = reader.Value;
break;
case "type":
eventTypeString = reader.Value;
break;
}
}
}
}
else
{
switch (reader.Name)
{
case "InputXml":
cookie = -1;
// Parse through here to get the cookie value
reader.Read();
if (reader.Name == "Request")
{
for (int i = 0; i < reader.AttributeCount; i++)
{
reader.MoveToAttribute(i);
if (reader.Name == "requestId")
{
Int32.TryParse(reader.Value, out cookie);
break;
}
}
}
if (cookie == -1)
{
Logger.Log("VoiceManager._DaemonPipe_OnReceiveLine(): Failed to parse InputXml for the cookie",
Helpers.LogLevel.Warning, Client);
}
break;
case "CaptureDevices":
_CaptureDevices.Clear();
break;
case "RenderDevices":
_RenderDevices.Clear();
break;
// case "ReturnCode":
// returnCode = reader.ReadElementContentAsInt();
// break;
case "StatusCode":
statusCode = reader.ReadElementContentAsInt();
break;
case "StatusString":
statusString = reader.ReadElementContentAsString();
break;
case "State":
state = reader.ReadElementContentAsInt();
break;
case "ConnectorHandle":
connectorHandle = reader.ReadElementContentAsString();
break;
case "AccountHandle":
accountHandle = reader.ReadElementContentAsString();
break;
case "SessionHandle":
sessionHandle = reader.ReadElementContentAsString();
break;
case "URI":
uriString = reader.ReadElementContentAsString();
break;
case "IsChannel":
isChannel = reader.ReadElementContentAsBoolean();
break;
case "Name":
nameString = reader.ReadElementContentAsString();
break;
// case "AudioMedia":
// audioMediaString = reader.ReadElementContentAsString();
// break;
case "ChannelName":
nameString = reader.ReadElementContentAsString();
break;
case "ParticipantURI":
uriString = reader.ReadElementContentAsString();
break;
case "DisplayName":
displayNameString = reader.ReadElementContentAsString();
break;
case "AccountName":
nameString = reader.ReadElementContentAsString();
break;
case "ParticipantType":
participantType = reader.ReadElementContentAsInt();
break;
case "IsLocallyMuted":
isLocallyMuted = reader.ReadElementContentAsBoolean();
break;
case "IsModeratorMuted":
isModeratorMuted = reader.ReadElementContentAsBoolean();
break;
case "IsSpeaking":
isSpeaking = reader.ReadElementContentAsBoolean();
break;
case "Volume":
volume = reader.ReadElementContentAsInt();
break;
case "Energy":
energy = reader.ReadElementContentAsFloat();
break;
case "MicEnergy":
energy = reader.ReadElementContentAsFloat();
break;
case "ChannelURI":
uriString = reader.ReadElementContentAsString();
break;
case "ChannelListResult":
_ChannelMap[nameString] = uriString;
break;
case "CaptureDevice":
reader.Read();
_CaptureDevices.Add(reader.ReadElementContentAsString());
break;
case "CurrentCaptureDevice":
reader.Read();
nameString = reader.ReadElementContentAsString();
break;
case "RenderDevice":
reader.Read();
_RenderDevices.Add(reader.ReadElementContentAsString());
break;
case "CurrentRenderDevice":
reader.Read();
nameString = reader.ReadElementContentAsString();
break;
}
}
break;
}
case XmlNodeType.EndElement:
if (reader.Depth == 0)
ProcessEvent();
break;
}
}
if (isEvent)
{
}
//Client.DebugLog("VOICE: " + line);
}
private void ProcessEvent()
{
if (isEvent)
{
switch (eventTypeString)
{
case "LoginStateChangeEvent":
if (OnLoginStateChange != null) OnLoginStateChange(cookie, accountHandle, statusCode, statusString, state);
break;
case "SessionNewEvent":
if (OnNewSession != null) OnNewSession(cookie, accountHandle, eventSessionHandle, state, nameString, uriString);
break;
case "SessionStateChangeEvent":
if (OnSessionStateChange != null) OnSessionStateChange(cookie, uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
break;
case "ParticipantStateChangeEvent":
if (OnParticipantStateChange != null) OnParticipantStateChange(cookie, uriString, statusCode, statusString, state, nameString, displayNameString, participantType);
break;
case "ParticipantPropertiesEvent":
if (OnParticipantProperties != null) OnParticipantProperties(cookie, uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
break;
case "AuxAudioPropertiesEvent":
if (OnAuxAudioProperties != null) OnAuxAudioProperties(cookie, energy);
break;
}
}
else
{
switch (actionString)
{
case "Connector.Create.1":
if (OnConnectorCreated != null) OnConnectorCreated(cookie, statusCode, statusString, connectorHandle);
break;
case "Account.Login.1":
if (OnLogin != null) OnLogin(cookie, statusCode, statusString, accountHandle);
break;
case "Session.Create.1":
if (OnSessionCreated != null) OnSessionCreated(cookie, statusCode, statusString, sessionHandle);
break;
case "Session.Connect.1":
if (OnSessionConnected != null) OnSessionConnected(cookie, statusCode, statusString);
break;
case "Session.Terminate.1":
if (OnSessionTerminated != null) OnSessionTerminated(cookie, statusCode, statusString);
break;
case "Account.Logout.1":
if (OnAccountLogout != null) OnAccountLogout(cookie, statusCode, statusString);
break;
case "Connector.InitiateShutdown.1":
if (OnConnectorInitiateShutdown != null) OnConnectorInitiateShutdown(cookie, statusCode, statusString);
break;
case "Account.ChannelGetList.1":
if (OnAccountChannelGetList != null) OnAccountChannelGetList(cookie, statusCode, statusString);
break;
case "Aux.GetCaptureDevices.1":
if (OnCaptureDevices != null) OnCaptureDevices(cookie, statusCode, statusString, nameString);
break;
case "Aux.GetRenderDevices.1":
if (OnRenderDevices != null) OnRenderDevices(cookie, statusCode, statusString, nameString);
break;
}
}
}
#endregion Callbacks
}
}
@@ -0,0 +1,157 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Utilities
{
public partial class VoiceManager
{
/// <summary>Amount of time to wait for the voice daemon to respond.
/// The value needs to stay relatively high because some of the calls
/// require the voice daemon to make remote queries before replying</summary>
public int BlockingTimeout = 30 * 1000;
protected Dictionary<int, AutoResetEvent> Events = new Dictionary<int, AutoResetEvent>();
public List<string> CaptureDevices()
{
AutoResetEvent evt = new AutoResetEvent(false);
Events[_CommandCookie] = evt;
if (RequestCaptureDevices() == -1)
{
Events.Remove(_CommandCookie);
return new List<string>();
}
if (evt.WaitOne(BlockingTimeout, false))
return CurrentCaptureDevices();
else
return new List<string>();
}
public List<string> RenderDevices()
{
AutoResetEvent evt = new AutoResetEvent(false);
Events[_CommandCookie] = evt;
if (RequestRenderDevices() == -1)
{
Events.Remove(_CommandCookie);
return new List<string>();
}
if (evt.WaitOne(BlockingTimeout, false))
return CurrentRenderDevices();
else
return new List<string>();
}
public string CreateConnector(out int status)
{
status = 0;
AutoResetEvent evt = new AutoResetEvent(false);
Events[_CommandCookie] = evt;
if (RequestCreateConnector() == -1)
{
Events.Remove(_CommandCookie);
return String.Empty;
}
bool success = evt.WaitOne(BlockingTimeout, false);
status = statusCode;
if (success && statusCode == 0)
return connectorHandle;
else
return String.Empty;
}
public string Login(string accountName, string password, string connectorHandle, out int status)
{
status = 0;
AutoResetEvent evt = new AutoResetEvent(false);
Events[_CommandCookie] = evt;
if (RequestLogin(accountName, password, connectorHandle) == -1)
{
Events.Remove(_CommandCookie);
return String.Empty;
}
bool success = evt.WaitOne(BlockingTimeout, false);
status = statusCode;
if (success && statusCode == 0)
return accountHandle;
else
return String.Empty;
}
protected void RegisterCallbacks()
{
OnCaptureDevices += new DevicesCallback(VoiceManager_OnCaptureDevices);
OnRenderDevices += new DevicesCallback(VoiceManager_OnRenderDevices);
OnConnectorCreated += new ConnectorCreatedCallback(VoiceManager_OnConnectorCreated);
OnLogin += new LoginCallback(VoiceManager_OnLogin);
}
#region Callbacks
private void VoiceManager_OnCaptureDevices(int cookie, int statusCode, string statusString, string currentDevice)
{
if (Events.ContainsKey(cookie))
Events[cookie].Set();
}
private void VoiceManager_OnRenderDevices(int cookie, int statusCode, string statusString, string currentDevice)
{
if (Events.ContainsKey(cookie))
Events[cookie].Set();
}
private void VoiceManager_OnConnectorCreated(int cookie, int statusCode, string statusString, string connectorHandle)
{
if (Events.ContainsKey(cookie))
Events[cookie].Set();
}
private void VoiceManager_OnLogin(int cookie, int statusCode, string statusString, string accountHandle)
{
if (Events.ContainsKey(cookie))
Events[cookie].Set();
}
#endregion Callbacks
}
}
File diff suppressed because it is too large Load Diff
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse
{
public partial class AgentManager
{
public partial class AgentMovement
{
/// <summary>
/// Camera controls for the agent, mostly a thin wrapper around
/// CoordinateFrame. This class is only responsible for state
/// tracking and math, it does not send any packets
/// </summary>
public class AgentCamera
{
/// <summary></summary>
public float Far;
/// <summary>The camera is a local frame of reference inside of
/// the larger grid space. This is where the math happens</summary>
private CoordinateFrame Frame;
/// <summary></summary>
public Vector3 Position
{
get { return Frame.Origin; }
set { Frame.Origin = value; }
}
/// <summary></summary>
public Vector3 AtAxis
{
get { return Frame.YAxis; }
set { Frame.YAxis = value; }
}
/// <summary></summary>
public Vector3 LeftAxis
{
get { return Frame.XAxis; }
set { Frame.XAxis = value; }
}
/// <summary></summary>
public Vector3 UpAxis
{
get { return Frame.ZAxis; }
set { Frame.ZAxis = value; }
}
/// <summary>
/// Default constructor
/// </summary>
public AgentCamera()
{
Frame = new CoordinateFrame(new Vector3(128f, 128f, 20f));
Far = 128f;
}
public void Roll(float angle)
{
Frame.Roll(angle);
}
public void Pitch(float angle)
{
Frame.Pitch(angle);
}
public void Yaw(float angle)
{
Frame.Yaw(angle);
}
public void LookDirection(Vector3 target)
{
Frame.LookDirection(target);
}
public void LookDirection(Vector3 target, Vector3 upDirection)
{
Frame.LookDirection(target, upDirection);
}
public void LookDirection(double heading)
{
Frame.LookDirection(heading);
}
public void LookAt(Vector3 position, Vector3 target)
{
Frame.LookAt(position, target);
}
public void LookAt(Vector3 position, Vector3 target, Vector3 upDirection)
{
Frame.LookAt(position, target, upDirection);
}
public void SetPositionOrientation(Vector3 position, float roll, float pitch, float yaw)
{
Frame.Origin = position;
Frame.ResetAxes();
Frame.Roll(roll);
Frame.Pitch(pitch);
Frame.Yaw(yaw);
}
}
}
}
}
+760
View File
@@ -0,0 +1,760 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenMetaverse
{
public partial class AgentManager
{
#region Enums
/// <summary>
/// Used to specify movement actions for your agent
/// </summary>
[Flags]
public enum ControlFlags
{
/// <summary>Empty flag</summary>
NONE = 0,
/// <summary>Move Forward (SL Keybinding: W/Up Arrow)</summary>
AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX,
/// <summary>Move Backward (SL Keybinding: S/Down Arrow)</summary>
AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX,
/// <summary>Move Left (SL Keybinding: Shift-(A/Left Arrow))</summary>
AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX,
/// <summary>Move Right (SL Keybinding: Shift-(D/Right Arrow))</summary>
AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX,
/// <summary>Not Flying: Jump/Flying: Move Up (SL Keybinding: E)</summary>
AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX,
/// <summary>Not Flying: Croutch/Flying: Move Down (SL Keybinding: C)</summary>
AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX,
/// <summary>Unused</summary>
AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX,
/// <summary>Unused</summary>
AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX,
/// <summary>Unused</summary>
AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX,
/// <summary>Unused</summary>
AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX,
/// <summary>ORed with AGENT_CONTROL_AT_* if the keyboard is being used</summary>
AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX,
/// <summary>ORed with AGENT_CONTROL_LEFT_* if the keyboard is being used</summary>
AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX,
/// <summary>ORed with AGENT_CONTROL_UP_* if the keyboard is being used</summary>
AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX,
/// <summary>Fly</summary>
AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX,
/// <summary></summary>
AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX,
/// <summary>Finish our current animation</summary>
AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX,
/// <summary>Stand up from the ground or a prim seat</summary>
AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX,
/// <summary>Sit on the ground at our current location</summary>
AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX,
/// <summary>Whether mouselook is currently enabled</summary>
AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX,
/// <summary>Legacy, used if a key was pressed for less than a certain amount of time</summary>
AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX,
/// <summary>Legacy, used if a key was pressed for less than a certain amount of time</summary>
AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX,
/// <summary>Legacy, used if a key was pressed for less than a certain amount of time</summary>
AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX,
/// <summary>Legacy, used if a key was pressed for less than a certain amount of time</summary>
AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX,
/// <summary>Legacy, used if a key was pressed for less than a certain amount of time</summary>
AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX,
/// <summary>Legacy, used if a key was pressed for less than a certain amount of time</summary>
AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX,
/// <summary></summary>
AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX,
/// <summary></summary>
AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX,
/// <summary>Set when the avatar is idled or set to away. Note that the away animation is
/// activated separately from setting this flag</summary>
AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX,
/// <summary></summary>
AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX,
/// <summary></summary>
AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX,
/// <summary></summary>
AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX,
/// <summary></summary>
AGENT_CONTROL_ML_LBUTTON_UP = 0x1 << CONTROL_ML_LBUTTON_UP_INDEX
}
#endregion Enums
#region AgentUpdate Constants
private const int CONTROL_AT_POS_INDEX = 0;
private const int CONTROL_AT_NEG_INDEX = 1;
private const int CONTROL_LEFT_POS_INDEX = 2;
private const int CONTROL_LEFT_NEG_INDEX = 3;
private const int CONTROL_UP_POS_INDEX = 4;
private const int CONTROL_UP_NEG_INDEX = 5;
private const int CONTROL_PITCH_POS_INDEX = 6;
private const int CONTROL_PITCH_NEG_INDEX = 7;
private const int CONTROL_YAW_POS_INDEX = 8;
private const int CONTROL_YAW_NEG_INDEX = 9;
private const int CONTROL_FAST_AT_INDEX = 10;
private const int CONTROL_FAST_LEFT_INDEX = 11;
private const int CONTROL_FAST_UP_INDEX = 12;
private const int CONTROL_FLY_INDEX = 13;
private const int CONTROL_STOP_INDEX = 14;
private const int CONTROL_FINISH_ANIM_INDEX = 15;
private const int CONTROL_STAND_UP_INDEX = 16;
private const int CONTROL_SIT_ON_GROUND_INDEX = 17;
private const int CONTROL_MOUSELOOK_INDEX = 18;
private const int CONTROL_NUDGE_AT_POS_INDEX = 19;
private const int CONTROL_NUDGE_AT_NEG_INDEX = 20;
private const int CONTROL_NUDGE_LEFT_POS_INDEX = 21;
private const int CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
private const int CONTROL_NUDGE_UP_POS_INDEX = 23;
private const int CONTROL_NUDGE_UP_NEG_INDEX = 24;
private const int CONTROL_TURN_LEFT_INDEX = 25;
private const int CONTROL_TURN_RIGHT_INDEX = 26;
private const int CONTROL_AWAY_INDEX = 27;
private const int CONTROL_LBUTTON_DOWN_INDEX = 28;
private const int CONTROL_LBUTTON_UP_INDEX = 29;
private const int CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
private const int CONTROL_ML_LBUTTON_UP_INDEX = 31;
private const int TOTAL_CONTROLS = 32;
#endregion AgentUpdate Constants
/// <summary>
/// Agent movement and camera control
///
/// Agent movement is controlled by setting specific <seealso cref="T:AgentManager.ControlFlags"/>
/// After the control flags are set, An AgentUpdate is required to update the simulator of the specified flags
/// This is most easily accomplished by setting one or more of the AgentMovement properties
///
/// Movement of an avatar is always based on a compass direction, for example AtPos will move the
/// agent from West to East or forward on the X Axis, AtNeg will of course move agent from
/// East to West or backward on the X Axis, LeftPos will be South to North or forward on the Y Axis
/// The Z axis is Up, finer grained control of movements can be done using the Nudge properties
/// </summary>
public partial class AgentMovement
{
#region Properties
/// <summary>Move agent positive along the X axis</summary>
public bool AtPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_POS, value); }
}
/// <summary>Move agent negative along the X axis</summary>
public bool AtNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG, value); }
}
/// <summary>Move agent positive along the Y axis</summary>
public bool LeftPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS, value); }
}
/// <summary>Move agent negative along the Y axis</summary>
public bool LeftNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, value); }
}
/// <summary>Move agent positive along the Z axis</summary>
public bool UpPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_POS, value); }
}
/// <summary>Move agent negative along the Z axis</summary>
public bool UpNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG, value); }
}
/// <summary></summary>
public bool PitchPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_POS, value); }
}
/// <summary></summary>
public bool PitchNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_NEG, value); }
}
/// <summary></summary>
public bool YawPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS, value); }
}
/// <summary></summary>
public bool YawNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG, value); }
}
/// <summary></summary>
public bool FastAt
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_AT); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_AT, value); }
}
/// <summary></summary>
public bool FastLeft
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_LEFT); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_LEFT, value); }
}
/// <summary></summary>
public bool FastUp
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_UP); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_UP, value); }
}
/// <summary>Causes simulator to make agent fly</summary>
public bool Fly
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FLY); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FLY, value); }
}
/// <summary>Stop movement</summary>
public bool Stop
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STOP); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STOP, value); }
}
/// <summary>Finish animation</summary>
public bool FinishAnim
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FINISH_ANIM); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FINISH_ANIM, value); }
}
/// <summary>Stand up from a sit</summary>
public bool StandUp
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP, value); }
}
/// <summary>Tells simulator to sit agent on ground</summary>
public bool SitOnGround
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND, value); }
}
/// <summary>Place agent into mouselook mode</summary>
public bool Mouselook
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK, value); }
}
/// <summary>Nudge agent positive along the X axis</summary>
public bool NudgeAtPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS, value); }
}
/// <summary>Nudge agent negative along the X axis</summary>
public bool NudgeAtNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG, value); }
}
/// <summary>Nudge agent positive along the Y axis</summary>
public bool NudgeLeftPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS, value); }
}
/// <summary>Nudge agent negative along the Y axis</summary>
public bool NudgeLeftNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG, value); }
}
/// <summary>Nudge agent positive along the Z axis</summary>
public bool NudgeUpPos
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_POS); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_POS, value); }
}
/// <summary>Nudge agent negative along the Z axis</summary>
public bool NudgeUpNeg
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG, value); }
}
/// <summary></summary>
public bool TurnLeft
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT, value); }
}
/// <summary></summary>
public bool TurnRight
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT, value); }
}
/// <summary>Tell simulator to mark agent as away</summary>
public bool Away
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AWAY); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AWAY, value); }
}
/// <summary></summary>
public bool LButtonDown
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN, value); }
}
/// <summary></summary>
public bool LButtonUp
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_UP); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_UP, value); }
}
/// <summary></summary>
public bool MLButtonDown
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_DOWN); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_DOWN, value); }
}
/// <summary></summary>
public bool MLButtonUp
{
get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_UP); }
set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_UP, value); }
}
/// <summary>
/// Returns "always run" value, or changes it by sending a SetAlwaysRunPacket
/// </summary>
public bool AlwaysRun
{
get
{
return alwaysRun;
}
set
{
alwaysRun = value;
SetAlwaysRunPacket run = new SetAlwaysRunPacket();
run.AgentData.AgentID = Client.Self.AgentID;
run.AgentData.SessionID = Client.Self.SessionID;
run.AgentData.AlwaysRun = alwaysRun;
Client.Network.SendPacket(run);
}
}
/// <summary>The current value of the agent control flags</summary>
public uint AgentControls
{
get { return agentControls; }
}
/// <summary>Gets or sets the interval in milliseconds at which
/// AgentUpdate packets are sent to the current simulator. Setting
/// this to a non-zero value will also enable the packet sending if
/// it was previously off, and setting it to zero will disable</summary>
public int UpdateInterval
{
get
{
return updateInterval;
}
set
{
if (value > 0)
{
if (updateTimer != null)
{
updateTimer.Change(value, value);
}
updateInterval = value;
}
else
{
if (updateTimer != null)
{
updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
updateInterval = 0;
}
}
}
/// <summary>Gets or sets whether AgentUpdate packets are sent to
/// the current simulator</summary>
public bool UpdateEnabled
{
get { return (updateInterval != 0); }
}
/// <summary>Reset movement controls every time we send an update</summary>
public bool AutoResetControls
{
get { return autoResetControls; }
set { autoResetControls = value; }
}
#endregion Properties
/// <summary>Agent camera controls</summary>
public AgentCamera Camera;
/// <summary>Currently only used for hiding your group title</summary>
public AgentFlags Flags = AgentFlags.None;
/// <summary>Action state of the avatar, which can currently be
/// typing and editing</summary>
public AgentState State = AgentState.None;
/// <summary></summary>
public Quaternion BodyRotation = Quaternion.Identity;
/// <summary></summary>
public Quaternion HeadRotation = Quaternion.Identity;
#region Change tracking
/// <summary></summary>
private Quaternion LastBodyRotation;
/// <summary></summary>
private Quaternion LastHeadRotation;
/// <summary></summary>
private Vector3 LastCameraCenter;
/// <summary></summary>
private Vector3 LastCameraXAxis;
/// <summary></summary>
private Vector3 LastCameraYAxis;
/// <summary></summary>
private Vector3 LastCameraZAxis;
/// <summary></summary>
private float LastFar;
#endregion Change tracking
private bool alwaysRun;
private GridClient Client;
private uint agentControls;
private int duplicateCount;
private AgentState lastState;
/// <summary>Timer for sending AgentUpdate packets</summary>
private Timer updateTimer;
private int updateInterval;
private bool autoResetControls;
/// <summary>Default constructor</summary>
public AgentMovement(GridClient client)
{
Client = client;
Camera = new AgentCamera();
Client.Network.LoginProgress += Network_OnConnected;
Client.Network.Disconnected += Network_OnDisconnected;
updateInterval = Settings.DEFAULT_AGENT_UPDATE_INTERVAL;
}
private void CleanupTimer()
{
if (updateTimer != null)
{
updateTimer.Dispose();
updateTimer = null;
}
}
private void Network_OnDisconnected(object sender, DisconnectedEventArgs e)
{
CleanupTimer();
}
private void Network_OnConnected(object sender, LoginProgressEventArgs e)
{
if (e.Status == LoginStatus.Success)
{
CleanupTimer();
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed), null, updateInterval, updateInterval);
}
}
/// <summary>
/// Send an AgentUpdate with the camera set at the current agent
/// position and pointing towards the heading specified
/// </summary>
/// <param name="heading">Camera rotation in radians</param>
/// <param name="reliable">Whether to send the AgentUpdate reliable
/// or not</param>
public void UpdateFromHeading(double heading, bool reliable)
{
Camera.Position = Client.Self.SimPosition;
Camera.LookDirection(heading);
BodyRotation.Z = (float)Math.Sin(heading / 2.0d);
BodyRotation.W = (float)Math.Cos(heading / 2.0d);
HeadRotation = BodyRotation;
SendUpdate(reliable);
}
/// <summary>
/// Rotates the avatar body and camera toward a target position.
/// This will also anchor the camera position on the avatar
/// </summary>
/// <param name="target">Region coordinates to turn toward</param>
public bool TurnToward(Vector3 target)
{
return TurnToward(target, true);
}
/// <summary>
/// Rotates the avatar body and camera toward a target position.
/// This will also anchor the camera position on the avatar
/// </summary>
/// <param name="target">Region coordinates to turn toward</param>
/// <param name="sendUpdate">whether to send update or not</param>
public bool TurnToward(Vector3 target, bool sendUpdate)
{
if (Client.Settings.SEND_AGENT_UPDATES)
{
Quaternion parentRot = Quaternion.Identity;
if (Client.Self.SittingOn > 0)
{
if (!Client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(Client.Self.SittingOn))
{
Logger.Log("Attempted TurnToward but parent prim is not in dictionary", Helpers.LogLevel.Warning, Client);
return false;
}
else parentRot = Client.Network.CurrentSim.ObjectsPrimitives[Client.Self.SittingOn].Rotation;
}
Quaternion between = Vector3.RotationBetween(Vector3.UnitX, Vector3.Normalize(target - Client.Self.SimPosition));
Quaternion rot = between * (Quaternion.Identity / parentRot);
BodyRotation = rot;
HeadRotation = rot;
Camera.LookAt(Client.Self.SimPosition, target);
if (sendUpdate) SendUpdate();
return true;
}
else
{
Logger.Log("Attempted TurnToward but agent updates are disabled", Helpers.LogLevel.Warning, Client);
return false;
}
}
/// <summary>
/// Send new AgentUpdate packet to update our current camera
/// position and rotation
/// </summary>
public void SendUpdate()
{
SendUpdate(false, Client.Network.CurrentSim);
}
/// <summary>
/// Send new AgentUpdate packet to update our current camera
/// position and rotation
/// </summary>
/// <param name="reliable">Whether to require server acknowledgement
/// of this packet</param>
public void SendUpdate(bool reliable)
{
SendUpdate(reliable, Client.Network.CurrentSim);
}
/// <summary>
/// Send new AgentUpdate packet to update our current camera
/// position and rotation
/// </summary>
/// <param name="reliable">Whether to require server acknowledgement
/// of this packet</param>
/// <param name="simulator">Simulator to send the update to</param>
public void SendUpdate(bool reliable, Simulator simulator)
{
// Since version 1.40.4 of the Linden simulator, sending this update
// causes corruption of the agent position in the simulator
if (simulator != null && (!simulator.AgentMovementComplete))
return;
Vector3 origin = Camera.Position;
Vector3 xAxis = Camera.LeftAxis;
Vector3 yAxis = Camera.AtAxis;
Vector3 zAxis = Camera.UpAxis;
// Attempted to sort these in a rough order of how often they might change
if (agentControls == 0 &&
yAxis == LastCameraYAxis &&
origin == LastCameraCenter &&
State == lastState &&
HeadRotation == LastHeadRotation &&
BodyRotation == LastBodyRotation &&
xAxis == LastCameraXAxis &&
Camera.Far == LastFar &&
zAxis == LastCameraZAxis)
{
++duplicateCount;
}
else
{
duplicateCount = 0;
}
if (Client.Settings.DISABLE_AGENT_UPDATE_DUPLICATE_CHECK || duplicateCount < 10)
{
// Store the current state to do duplicate checking
LastHeadRotation = HeadRotation;
LastBodyRotation = BodyRotation;
LastCameraYAxis = yAxis;
LastCameraCenter = origin;
LastCameraXAxis = xAxis;
LastCameraZAxis = zAxis;
LastFar = Camera.Far;
lastState = State;
// Build the AgentUpdate packet and send it
AgentUpdatePacket update = new AgentUpdatePacket();
update.Header.Reliable = reliable;
update.AgentData.AgentID = Client.Self.AgentID;
update.AgentData.SessionID = Client.Self.SessionID;
update.AgentData.HeadRotation = HeadRotation;
update.AgentData.BodyRotation = BodyRotation;
update.AgentData.CameraAtAxis = xAxis;
update.AgentData.CameraCenter = origin;
update.AgentData.CameraLeftAxis = yAxis;
update.AgentData.CameraUpAxis = zAxis;
update.AgentData.Far = Camera.Far;
update.AgentData.State = (byte)State;
update.AgentData.ControlFlags = agentControls;
update.AgentData.Flags = (byte)Flags;
Client.Network.SendPacket(update, simulator);
if (autoResetControls) {
ResetControlFlags();
}
}
}
/// <summary>
/// Builds an AgentUpdate packet entirely from parameters. This
/// will not touch the state of Self.Movement or
/// Self.Movement.Camera in any way
/// </summary>
/// <param name="controlFlags"></param>
/// <param name="position"></param>
/// <param name="forwardAxis"></param>
/// <param name="leftAxis"></param>
/// <param name="upAxis"></param>
/// <param name="bodyRotation"></param>
/// <param name="headRotation"></param>
/// <param name="farClip"></param>
/// <param name="reliable"></param>
/// <param name="flags"></param>
/// <param name="state"></param>
public void SendManualUpdate(AgentManager.ControlFlags controlFlags, Vector3 position, Vector3 forwardAxis,
Vector3 leftAxis, Vector3 upAxis, Quaternion bodyRotation, Quaternion headRotation, float farClip,
AgentFlags flags, AgentState state, bool reliable)
{
// Since version 1.40.4 of the Linden simulator, sending this update
// causes corruption of the agent position in the simulator
if (Client.Network.CurrentSim != null && (!Client.Network.CurrentSim.HandshakeComplete))
return;
AgentUpdatePacket update = new AgentUpdatePacket();
update.AgentData.AgentID = Client.Self.AgentID;
update.AgentData.SessionID = Client.Self.SessionID;
update.AgentData.BodyRotation = bodyRotation;
update.AgentData.HeadRotation = headRotation;
update.AgentData.CameraCenter = position;
update.AgentData.CameraAtAxis = forwardAxis;
update.AgentData.CameraLeftAxis = leftAxis;
update.AgentData.CameraUpAxis = upAxis;
update.AgentData.Far = farClip;
update.AgentData.ControlFlags = (uint)controlFlags;
update.AgentData.Flags = (byte)flags;
update.AgentData.State = (byte)state;
update.Header.Reliable = reliable;
Client.Network.SendPacket(update);
}
private bool GetControlFlag(ControlFlags flag)
{
return (agentControls & (uint)flag) != 0;
}
private void SetControlFlag(ControlFlags flag, bool value)
{
if (value) agentControls |= (uint)flag;
else agentControls &= ~((uint)flag);
}
public void ResetControlFlags()
{
// Reset all of the flags except for persistent settings like
// away, fly, mouselook, and crouching
agentControls &=
(uint)(ControlFlags.AGENT_CONTROL_AWAY |
ControlFlags.AGENT_CONTROL_FLY |
ControlFlags.AGENT_CONTROL_MOUSELOOK |
ControlFlags.AGENT_CONTROL_UP_NEG);
}
/// <summary>
/// Sends update of Field of Vision vertical angle to the simulator
/// </summary>
/// <param name="angle">Angle in radians</param>
public void SetFOVVerticalAngle(float angle)
{
OpenMetaverse.Packets.AgentFOVPacket msg = new OpenMetaverse.Packets.AgentFOVPacket();
msg.AgentData.AgentID = Client.Self.AgentID;
msg.AgentData.SessionID = Client.Self.SessionID;
msg.AgentData.CircuitCode = Client.Network.CircuitCode;
msg.FOVBlock.GenCounter = 0;
msg.FOVBlock.VerticalAngle = angle;
Client.Network.SendPacket(msg);
}
private void UpdateTimer_Elapsed(object obj)
{
if (Client.Network.Connected && Client.Settings.SEND_AGENT_UPDATES)
{
//Send an AgentUpdate packet
SendUpdate(false, Client.Network.CurrentSim);
}
}
}
}
}
+236
View File
@@ -0,0 +1,236 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse.Packets;
namespace OpenMetaverse
{
/// <summary>
/// Throttles the network traffic for various different traffic types.
/// Access this class through GridClient.Throttle
/// </summary>
public class AgentThrottle
{
/// <summary>Maximum bits per second for resending unacknowledged packets</summary>
public float Resend
{
get { return resend; }
set
{
if (value > 150000.0f) resend = 150000.0f;
else if (value < 10000.0f) resend = 10000.0f;
else resend = value;
}
}
/// <summary>Maximum bits per second for LayerData terrain</summary>
public float Land
{
get { return land; }
set
{
if (value > 170000.0f) land = 170000.0f;
else if (value < 0.0f) land = 0.0f; // We don't have control of these so allow throttling to 0
else land = value;
}
}
/// <summary>Maximum bits per second for LayerData wind data</summary>
public float Wind
{
get { return wind; }
set
{
if (value > 34000.0f) wind = 34000.0f;
else if (value < 0.0f) wind = 0.0f; // We don't have control of these so allow throttling to 0
else wind = value;
}
}
/// <summary>Maximum bits per second for LayerData clouds</summary>
public float Cloud
{
get { return cloud; }
set
{
if (value > 34000.0f) cloud = 34000.0f;
else if (value < 0.0f) cloud = 0.0f; // We don't have control of these so allow throttling to 0
else cloud = value;
}
}
/// <summary>Unknown, includes object data</summary>
public float Task
{
get { return task; }
set
{
if (value > 446000.0f) task = 446000.0f;
else if (value < 4000.0f) task = 4000.0f;
else task = value;
}
}
/// <summary>Maximum bits per second for textures</summary>
public float Texture
{
get { return texture; }
set
{
if (value > 446000.0f) texture = 446000.0f;
else if (value < 4000.0f) texture = 4000.0f;
else texture = value;
}
}
/// <summary>Maximum bits per second for downloaded assets</summary>
public float Asset
{
get { return asset; }
set
{
if (value > 220000.0f) asset = 220000.0f;
else if (value < 10000.0f) asset = 10000.0f;
else asset = value;
}
}
/// <summary>Maximum bits per second the entire connection, divided up
/// between invidiual streams using default multipliers</summary>
public float Total
{
get { return Resend + Land + Wind + Cloud + Task + Texture + Asset; }
set
{
// Sane initial values
Resend = (value * 0.1f);
Land = (float)(value * 0.52f / 3f);
Wind = (float)(value * 0.05f);
Cloud = (float)(value * 0.05f);
Task = (float)(value * 0.704f / 3f);
Texture = (float)(value * 0.704f / 3f);
Asset = (float)(value * 0.484f / 3f);
}
}
private GridClient Client;
private float resend;
private float land;
private float wind;
private float cloud;
private float task;
private float texture;
private float asset;
/// <summary>
/// Default constructor, uses a default high total of 1500 KBps (1536000)
/// </summary>
public AgentThrottle(GridClient client)
{
Client = client;
Total = 1536000.0f;
}
/// <summary>
/// Constructor that decodes an existing AgentThrottle packet in to
/// individual values
/// </summary>
/// <param name="data">Reference to the throttle data in an AgentThrottle
/// packet</param>
/// <param name="pos">Offset position to start reading at in the
/// throttle data</param>
/// <remarks>This is generally not needed in clients as the server will
/// never send a throttle packet to the client</remarks>
public AgentThrottle(byte[] data, int pos)
{
byte[] adjData;
if (!BitConverter.IsLittleEndian)
{
byte[] newData = new byte[7 * 4];
Buffer.BlockCopy(data, pos, newData, 0, 7 * 4);
for (int i = 0; i < 7; i++)
Array.Reverse(newData, i * 4, 4);
adjData = newData;
}
else
{
adjData = data;
}
Resend = BitConverter.ToSingle(adjData, pos); pos += 4;
Land = BitConverter.ToSingle(adjData, pos); pos += 4;
Wind = BitConverter.ToSingle(adjData, pos); pos += 4;
Cloud = BitConverter.ToSingle(adjData, pos); pos += 4;
Task = BitConverter.ToSingle(adjData, pos); pos += 4;
Texture = BitConverter.ToSingle(adjData, pos); pos += 4;
Asset = BitConverter.ToSingle(adjData, pos);
}
/// <summary>
/// Send an AgentThrottle packet to the current server using the
/// current values
/// </summary>
public void Set()
{
Set(Client.Network.CurrentSim);
}
/// <summary>
/// Send an AgentThrottle packet to the specified server using the
/// current values
/// </summary>
public void Set(Simulator simulator)
{
AgentThrottlePacket throttle = new AgentThrottlePacket();
throttle.AgentData.AgentID = Client.Self.AgentID;
throttle.AgentData.SessionID = Client.Self.SessionID;
throttle.AgentData.CircuitCode = Client.Network.CircuitCode;
throttle.Throttle.GenCounter = 0;
throttle.Throttle.Throttles = this.ToBytes();
Client.Network.SendPacket(throttle, simulator);
}
/// <summary>
/// Convert the current throttle values to a byte array that can be put
/// in an AgentThrottle packet
/// </summary>
/// <returns>Byte array containing all the throttle values</returns>
public byte[] ToBytes()
{
byte[] data = new byte[7 * 4];
int i = 0;
Buffer.BlockCopy(Utils.FloatToBytes(Resend), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes(Land), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes(Wind), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes(Cloud), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes(Task), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes(Texture), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes(Asset), 0, data, i, 4); i += 4;
return data;
}
}
}
+326
View File
@@ -0,0 +1,326 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Reflection;
using System.Collections.Generic;
namespace OpenMetaverse
{
/// <summary>
/// Static pre-defined animations available to all agents
/// </summary>
public static class Animations
{
/// <summary>Agent with afraid expression on face</summary>
public readonly static UUID AFRAID = new UUID("6b61c8e8-4747-0d75-12d7-e49ff207a4ca");
/// <summary>Agent aiming a bazooka (right handed)</summary>
public readonly static UUID AIM_BAZOOKA_R = new UUID("b5b4a67d-0aee-30d2-72cd-77b333e932ef");
/// <summary>Agent aiming a bow (left handed)</summary>
public readonly static UUID AIM_BOW_L = new UUID("46bb4359-de38-4ed8-6a22-f1f52fe8f506");
/// <summary>Agent aiming a hand gun (right handed)</summary>
public readonly static UUID AIM_HANDGUN_R = new UUID("3147d815-6338-b932-f011-16b56d9ac18b");
/// <summary>Agent aiming a rifle (right handed)</summary>
public readonly static UUID AIM_RIFLE_R = new UUID("ea633413-8006-180a-c3ba-96dd1d756720");
/// <summary>Agent with angry expression on face</summary>
public readonly static UUID ANGRY = new UUID("5747a48e-073e-c331-f6f3-7c2149613d3e");
/// <summary>Agent hunched over (away)</summary>
public readonly static UUID AWAY = new UUID("fd037134-85d4-f241-72c6-4f42164fedee");
/// <summary>Agent doing a backflip</summary>
public readonly static UUID BACKFLIP = new UUID("c4ca6188-9127-4f31-0158-23c4e2f93304");
/// <summary>Agent laughing while holding belly</summary>
public readonly static UUID BELLY_LAUGH = new UUID("18b3a4b5-b463-bd48-e4b6-71eaac76c515");
/// <summary>Agent blowing a kiss</summary>
public readonly static UUID BLOW_KISS = new UUID("db84829b-462c-ee83-1e27-9bbee66bd624");
/// <summary>Agent with bored expression on face</summary>
public readonly static UUID BORED = new UUID("b906c4ba-703b-1940-32a3-0c7f7d791510");
/// <summary>Agent bowing to audience</summary>
public readonly static UUID BOW = new UUID("82e99230-c906-1403-4d9c-3889dd98daba");
/// <summary>Agent brushing himself/herself off</summary>
public readonly static UUID BRUSH = new UUID("349a3801-54f9-bf2c-3bd0-1ac89772af01");
/// <summary>Agent in busy mode</summary>
public readonly static UUID BUSY = new UUID("efcf670c-2d18-8128-973a-034ebc806b67");
/// <summary>Agent clapping hands</summary>
public readonly static UUID CLAP = new UUID("9b0c1c4e-8ac7-7969-1494-28c874c4f668");
/// <summary>Agent doing a curtsey bow</summary>
public readonly static UUID COURTBOW = new UUID("9ba1c942-08be-e43a-fb29-16ad440efc50");
/// <summary>Agent crouching</summary>
public readonly static UUID CROUCH = new UUID("201f3fdf-cb1f-dbec-201f-7333e328ae7c");
/// <summary>Agent crouching while walking</summary>
public readonly static UUID CROUCHWALK = new UUID("47f5f6fb-22e5-ae44-f871-73aaaf4a6022");
/// <summary>Agent crying</summary>
public readonly static UUID CRY = new UUID("92624d3e-1068-f1aa-a5ec-8244585193ed");
/// <summary>Agent unanimated with arms out (e.g. setting appearance)</summary>
public readonly static UUID CUSTOMIZE = new UUID("038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53");
/// <summary>Agent re-animated after set appearance finished</summary>
public readonly static UUID CUSTOMIZE_DONE = new UUID("6883a61a-b27b-5914-a61e-dda118a9ee2c");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE1 = new UUID("b68a3d7c-de9e-fc87-eec8-543d787e5b0d");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE2 = new UUID("928cae18-e31d-76fd-9cc9-2f55160ff818");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE3 = new UUID("30047778-10ea-1af7-6881-4db7a3a5a114");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE4 = new UUID("951469f4-c7b2-c818-9dee-ad7eea8c30b7");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE5 = new UUID("4bd69a1d-1114-a0b4-625f-84e0a5237155");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE6 = new UUID("cd28b69b-9c95-bb78-3f94-8d605ff1bb12");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE7 = new UUID("a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6");
/// <summary>Agent dancing</summary>
public readonly static UUID DANCE8 = new UUID("b0dc417c-1f11-af36-2e80-7e7489fa7cdc");
/// <summary>Agent on ground unanimated</summary>
public readonly static UUID DEAD = new UUID("57abaae6-1d17-7b1b-5f98-6d11a6411276");
/// <summary>Agent boozing it up</summary>
public readonly static UUID DRINK = new UUID("0f86e355-dd31-a61c-fdb0-3a96b9aad05f");
/// <summary>Agent with embarassed expression on face</summary>
public readonly static UUID EMBARRASSED = new UUID("514af488-9051-044a-b3fc-d4dbf76377c6");
/// <summary>Agent with afraid expression on face</summary>
public readonly static UUID EXPRESS_AFRAID = new UUID("aa2df84d-cf8f-7218-527b-424a52de766e");
/// <summary>Agent with angry expression on face</summary>
public readonly static UUID EXPRESS_ANGER = new UUID("1a03b575-9634-b62a-5767-3a679e81f4de");
/// <summary>Agent with bored expression on face</summary>
public readonly static UUID EXPRESS_BORED = new UUID("214aa6c1-ba6a-4578-f27c-ce7688f61d0d");
/// <summary>Agent crying</summary>
public readonly static UUID EXPRESS_CRY = new UUID("d535471b-85bf-3b4d-a542-93bea4f59d33");
/// <summary>Agent showing disdain (dislike) for something</summary>
public readonly static UUID EXPRESS_DISDAIN = new UUID("d4416ff1-09d3-300f-4183-1b68a19b9fc1");
/// <summary>Agent with embarassed expression on face</summary>
public readonly static UUID EXPRESS_EMBARRASSED = new UUID("0b8c8211-d78c-33e8-fa28-c51a9594e424");
/// <summary>Agent with frowning expression on face</summary>
public readonly static UUID EXPRESS_FROWN = new UUID("fee3df48-fa3d-1015-1e26-a205810e3001");
/// <summary>Agent with kissy face</summary>
public readonly static UUID EXPRESS_KISS = new UUID("1e8d90cc-a84e-e135-884c-7c82c8b03a14");
/// <summary>Agent expressing laughgter</summary>
public readonly static UUID EXPRESS_LAUGH = new UUID("62570842-0950-96f8-341c-809e65110823");
/// <summary>Agent with open mouth</summary>
public readonly static UUID EXPRESS_OPEN_MOUTH = new UUID("d63bc1f9-fc81-9625-a0c6-007176d82eb7");
/// <summary>Agent with repulsed expression on face</summary>
public readonly static UUID EXPRESS_REPULSED = new UUID("f76cda94-41d4-a229-2872-e0296e58afe1");
/// <summary>Agent expressing sadness</summary>
public readonly static UUID EXPRESS_SAD = new UUID("eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7");
/// <summary>Agent shrugging shoulders</summary>
public readonly static UUID EXPRESS_SHRUG = new UUID("a351b1bc-cc94-aac2-7bea-a7e6ebad15ef");
/// <summary>Agent with a smile</summary>
public readonly static UUID EXPRESS_SMILE = new UUID("b7c7c833-e3d3-c4e3-9fc0-131237446312");
/// <summary>Agent expressing surprise</summary>
public readonly static UUID EXPRESS_SURPRISE = new UUID("728646d9-cc79-08b2-32d6-937f0a835c24");
/// <summary>Agent sticking tongue out</summary>
public readonly static UUID EXPRESS_TONGUE_OUT = new UUID("835965c6-7f2f-bda2-5deb-2478737f91bf");
/// <summary>Agent with big toothy smile</summary>
public readonly static UUID EXPRESS_TOOTHSMILE = new UUID("b92ec1a5-e7ce-a76b-2b05-bcdb9311417e");
/// <summary>Agent winking</summary>
public readonly static UUID EXPRESS_WINK = new UUID("da020525-4d94-59d6-23d7-81fdebf33148");
/// <summary>Agent expressing worry</summary>
public readonly static UUID EXPRESS_WORRY = new UUID("9c05e5c7-6f07-6ca4-ed5a-b230390c3950");
/// <summary>Agent falling down</summary>
public readonly static UUID FALLDOWN = new UUID("666307d9-a860-572d-6fd4-c3ab8865c094");
/// <summary>Agent walking (feminine version)</summary>
public readonly static UUID FEMALE_WALK = new UUID("f5fc7433-043d-e819-8298-f519a119b688");
/// <summary>Agent wagging finger (disapproval)</summary>
public readonly static UUID FINGER_WAG = new UUID("c1bc7f36-3ba0-d844-f93c-93be945d644f");
/// <summary>I'm not sure I want to know</summary>
public readonly static UUID FIST_PUMP = new UUID("7db00ccd-f380-f3ee-439d-61968ec69c8a");
/// <summary>Agent in superman position</summary>
public readonly static UUID FLY = new UUID("aec4610c-757f-bc4e-c092-c6e9caf18daf");
/// <summary>Agent in superman position</summary>
public readonly static UUID FLYSLOW = new UUID("2b5a38b2-5e00-3a97-a495-4c826bc443e6");
/// <summary>Agent greeting another</summary>
public readonly static UUID HELLO = new UUID("9b29cd61-c45b-5689-ded2-91756b8d76a9");
/// <summary>Agent holding bazooka (right handed)</summary>
public readonly static UUID HOLD_BAZOOKA_R = new UUID("ef62d355-c815-4816-2474-b1acc21094a6");
/// <summary>Agent holding a bow (left handed)</summary>
public readonly static UUID HOLD_BOW_L = new UUID("8b102617-bcba-037b-86c1-b76219f90c88");
/// <summary>Agent holding a handgun (right handed)</summary>
public readonly static UUID HOLD_HANDGUN_R = new UUID("efdc1727-8b8a-c800-4077-975fc27ee2f2");
/// <summary>Agent holding a rifle (right handed)</summary>
public readonly static UUID HOLD_RIFLE_R = new UUID("3d94bad0-c55b-7dcc-8763-033c59405d33");
/// <summary>Agent throwing an object (right handed)</summary>
public readonly static UUID HOLD_THROW_R = new UUID("7570c7b5-1f22-56dd-56ef-a9168241bbb6");
/// <summary>Agent in static hover</summary>
public readonly static UUID HOVER = new UUID("4ae8016b-31b9-03bb-c401-b1ea941db41d");
/// <summary>Agent hovering downward</summary>
public readonly static UUID HOVER_DOWN = new UUID("20f063ea-8306-2562-0b07-5c853b37b31e");
/// <summary>Agent hovering upward</summary>
public readonly static UUID HOVER_UP = new UUID("62c5de58-cb33-5743-3d07-9e4cd4352864");
/// <summary>Agent being impatient</summary>
public readonly static UUID IMPATIENT = new UUID("5ea3991f-c293-392e-6860-91dfa01278a3");
/// <summary>Agent jumping</summary>
public readonly static UUID JUMP = new UUID("2305bd75-1ca9-b03b-1faa-b176b8a8c49e");
/// <summary>Agent jumping with fervor</summary>
public readonly static UUID JUMP_FOR_JOY = new UUID("709ea28e-1573-c023-8bf8-520c8bc637fa");
/// <summary>Agent point to lips then rear end</summary>
public readonly static UUID KISS_MY_BUTT = new UUID("19999406-3a3a-d58c-a2ac-d72e555dcf51");
/// <summary>Agent landing from jump, finished flight, etc</summary>
public readonly static UUID LAND = new UUID("7a17b059-12b2-41b1-570a-186368b6aa6f");
/// <summary>Agent laughing</summary>
public readonly static UUID LAUGH_SHORT = new UUID("ca5b3f14-3194-7a2b-c894-aa699b718d1f");
/// <summary>Agent landing from jump, finished flight, etc</summary>
public readonly static UUID MEDIUM_LAND = new UUID("f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57");
/// <summary>Agent sitting on a motorcycle</summary>
public readonly static UUID MOTORCYCLE_SIT = new UUID("08464f78-3a8e-2944-cba5-0c94aff3af29");
/// <summary></summary>
public readonly static UUID MUSCLE_BEACH = new UUID("315c3a41-a5f3-0ba4-27da-f893f769e69b");
/// <summary>Agent moving head side to side</summary>
public readonly static UUID NO = new UUID("5a977ed9-7f72-44e9-4c4c-6e913df8ae74");
/// <summary>Agent moving head side to side with unhappy expression</summary>
public readonly static UUID NO_UNHAPPY = new UUID("d83fa0e5-97ed-7eb2-e798-7bd006215cb4");
/// <summary>Agent taunting another</summary>
public readonly static UUID NYAH_NYAH = new UUID("f061723d-0a18-754f-66ee-29a44795a32f");
/// <summary></summary>
public readonly static UUID ONETWO_PUNCH = new UUID("eefc79be-daae-a239-8c04-890f5d23654a");
/// <summary>Agent giving peace sign</summary>
public readonly static UUID PEACE = new UUID("b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9");
/// <summary>Agent pointing at self</summary>
public readonly static UUID POINT_ME = new UUID("17c024cc-eef2-f6a0-3527-9869876d7752");
/// <summary>Agent pointing at another</summary>
public readonly static UUID POINT_YOU = new UUID("ec952cca-61ef-aa3b-2789-4d1344f016de");
/// <summary>Agent preparing for jump (bending knees)</summary>
public readonly static UUID PRE_JUMP = new UUID("7a4e87fe-de39-6fcb-6223-024b00893244");
/// <summary>Agent punching with left hand</summary>
public readonly static UUID PUNCH_LEFT = new UUID("f3300ad9-3462-1d07-2044-0fef80062da0");
/// <summary>Agent punching with right hand</summary>
public readonly static UUID PUNCH_RIGHT = new UUID("c8e42d32-7310-6906-c903-cab5d4a34656");
/// <summary>Agent acting repulsed</summary>
public readonly static UUID REPULSED = new UUID("36f81a92-f076-5893-dc4b-7c3795e487cf");
/// <summary>Agent trying to be Chuck Norris</summary>
public readonly static UUID ROUNDHOUSE_KICK = new UUID("49aea43b-5ac3-8a44-b595-96100af0beda");
/// <summary>Rocks, Paper, Scissors 1, 2, 3</summary>
public readonly static UUID RPS_COUNTDOWN = new UUID("35db4f7e-28c2-6679-cea9-3ee108f7fc7f");
/// <summary>Agent with hand flat over other hand</summary>
public readonly static UUID RPS_PAPER = new UUID("0836b67f-7f7b-f37b-c00a-460dc1521f5a");
/// <summary>Agent with fist over other hand</summary>
public readonly static UUID RPS_ROCK = new UUID("42dd95d5-0bc6-6392-f650-777304946c0f");
/// <summary>Agent with two fingers spread over other hand</summary>
public readonly static UUID RPS_SCISSORS = new UUID("16803a9f-5140-e042-4d7b-d28ba247c325");
/// <summary>Agent running</summary>
public readonly static UUID RUN = new UUID("05ddbff8-aaa9-92a1-2b74-8fe77a29b445");
/// <summary>Agent appearing sad</summary>
public readonly static UUID SAD = new UUID("0eb702e2-cc5a-9a88-56a5-661a55c0676a");
/// <summary>Agent saluting</summary>
public readonly static UUID SALUTE = new UUID("cd7668a6-7011-d7e2-ead8-fc69eff1a104");
/// <summary>Agent shooting bow (left handed)</summary>
public readonly static UUID SHOOT_BOW_L = new UUID("e04d450d-fdb5-0432-fd68-818aaf5935f8");
/// <summary>Agent cupping mouth as if shouting</summary>
public readonly static UUID SHOUT = new UUID("6bd01860-4ebd-127a-bb3d-d1427e8e0c42");
/// <summary>Agent shrugging shoulders</summary>
public readonly static UUID SHRUG = new UUID("70ea714f-3a97-d742-1b01-590a8fcd1db5");
/// <summary>Agent in sit position</summary>
public readonly static UUID SIT = new UUID("1a5fe8ac-a804-8a5d-7cbd-56bd83184568");
/// <summary>Agent in sit position (feminine)</summary>
public readonly static UUID SIT_FEMALE = new UUID("b1709c8d-ecd3-54a1-4f28-d55ac0840782");
/// <summary>Agent in sit position (generic)</summary>
public readonly static UUID SIT_GENERIC = new UUID("245f3c54-f1c0-bf2e-811f-46d8eeb386e7");
/// <summary>Agent sitting on ground</summary>
public readonly static UUID SIT_GROUND = new UUID("1c7600d6-661f-b87b-efe2-d7421eb93c86");
/// <summary>Agent sitting on ground</summary>
public readonly static UUID SIT_GROUND_staticRAINED = new UUID("1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e");
/// <summary></summary>
public readonly static UUID SIT_TO_STAND = new UUID("a8dee56f-2eae-9e7a-05a2-6fb92b97e21e");
/// <summary>Agent sleeping on side</summary>
public readonly static UUID SLEEP = new UUID("f2bed5f9-9d44-39af-b0cd-257b2a17fe40");
/// <summary>Agent smoking</summary>
public readonly static UUID SMOKE_IDLE = new UUID("d2f2ee58-8ad1-06c9-d8d3-3827ba31567a");
/// <summary>Agent inhaling smoke</summary>
public readonly static UUID SMOKE_INHALE = new UUID("6802d553-49da-0778-9f85-1599a2266526");
/// <summary></summary>
public readonly static UUID SMOKE_THROW_DOWN = new UUID("0a9fb970-8b44-9114-d3a9-bf69cfe804d6");
/// <summary>Agent taking a picture</summary>
public readonly static UUID SNAPSHOT = new UUID("eae8905b-271a-99e2-4c0e-31106afd100c");
/// <summary>Agent standing</summary>
public readonly static UUID STAND = new UUID("2408fe9e-df1d-1d7d-f4ff-1384fa7b350f");
/// <summary>Agent standing up</summary>
public readonly static UUID STANDUP = new UUID("3da1d753-028a-5446-24f3-9c9b856d9422");
/// <summary>Agent standing</summary>
public readonly static UUID STAND_1 = new UUID("15468e00-3400-bb66-cecc-646d7c14458e");
/// <summary>Agent standing</summary>
public readonly static UUID STAND_2 = new UUID("370f3a20-6ca6-9971-848c-9a01bc42ae3c");
/// <summary>Agent standing</summary>
public readonly static UUID STAND_3 = new UUID("42b46214-4b44-79ae-deb8-0df61424ff4b");
/// <summary>Agent standing</summary>
public readonly static UUID STAND_4 = new UUID("f22fed8b-a5ed-2c93-64d5-bdd8b93c889f");
/// <summary>Agent stretching</summary>
public readonly static UUID STRETCH = new UUID("80700431-74ec-a008-14f8-77575e73693f");
/// <summary>Agent in stride (fast walk)</summary>
public readonly static UUID STRIDE = new UUID("1cb562b0-ba21-2202-efb3-30f82cdf9595");
/// <summary>Agent surfing</summary>
public readonly static UUID SURF = new UUID("41426836-7437-7e89-025d-0aa4d10f1d69");
/// <summary>Agent acting surprised</summary>
public readonly static UUID SURPRISE = new UUID("313b9881-4302-73c0-c7d0-0e7a36b6c224");
/// <summary>Agent striking with a sword</summary>
public readonly static UUID SWORD_STRIKE = new UUID("85428680-6bf9-3e64-b489-6f81087c24bd");
/// <summary>Agent talking (lips moving)</summary>
public readonly static UUID TALK = new UUID("5c682a95-6da4-a463-0bf6-0f5b7be129d1");
/// <summary>Agent throwing a tantrum</summary>
public readonly static UUID TANTRUM = new UUID("11000694-3f41-adc2-606b-eee1d66f3724");
/// <summary>Agent throwing an object (right handed)</summary>
public readonly static UUID THROW_R = new UUID("aa134404-7dac-7aca-2cba-435f9db875ca");
/// <summary>Agent trying on a shirt</summary>
public readonly static UUID TRYON_SHIRT = new UUID("83ff59fe-2346-f236-9009-4e3608af64c1");
/// <summary>Agent turning to the left</summary>
public readonly static UUID TURNLEFT = new UUID("56e0ba0d-4a9f-7f27-6117-32f2ebbf6135");
/// <summary>Agent turning to the right</summary>
public readonly static UUID TURNRIGHT = new UUID("2d6daa51-3192-6794-8e2e-a15f8338ec30");
/// <summary>Agent typing</summary>
public readonly static UUID TYPE = new UUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
/// <summary>Agent walking</summary>
public readonly static UUID WALK = new UUID("6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0");
/// <summary>Agent whispering</summary>
public readonly static UUID WHISPER = new UUID("7693f268-06c7-ea71-fa21-2b30d6533f8f");
/// <summary>Agent whispering with fingers in mouth</summary>
public readonly static UUID WHISTLE = new UUID("b1ed7982-c68e-a982-7561-52a88a5298c0");
/// <summary>Agent winking</summary>
public readonly static UUID WINK = new UUID("869ecdad-a44b-671e-3266-56aef2e3ac2e");
/// <summary>Agent winking</summary>
public readonly static UUID WINK_HOLLYWOOD = new UUID("c0c4030f-c02b-49de-24ba-2331f43fe41c");
/// <summary>Agent worried</summary>
public readonly static UUID WORRY = new UUID("9f496bd2-589a-709f-16cc-69bf7df1d36c");
/// <summary>Agent nodding yes</summary>
public readonly static UUID YES = new UUID("15dd911d-be82-2856-26db-27659b142875");
/// <summary>Agent nodding yes with happy face</summary>
public readonly static UUID YES_HAPPY = new UUID("b8c8b2a3-9008-1771-3bfc-90924955ab2d");
/// <summary>Agent floating with legs and arms crossed</summary>
public readonly static UUID YOGA_FLOAT = new UUID("42ecd00b-9947-a97c-400a-bbc9174c7aeb");
/// <summary>
/// A dictionary containing all pre-defined animations
/// </summary>
/// <returns>A dictionary containing the pre-defined animations,
/// where the key is the animations ID, and the value is a string
/// containing a name to identify the purpose of the animation</returns>
public static Dictionary<UUID, string> ToDictionary()
{
Dictionary<UUID, string> dict = new Dictionary<UUID, string>();
Type type = typeof(Animations);
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
dict.Add((UUID)field.GetValue(type), field.Name);
}
return dict;
}
}
}
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenMetaverse")]
[assembly: AssemblyDescription("OpenMetaverse library")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("openmetaverse.org")]
[assembly: AssemblyProduct("OpenMetaverse")]
[assembly: AssemblyCopyright("Copyright © openmetaverse.org 2006-2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("0.9.3.0")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]
[assembly: AssemblyFileVersionAttribute("0.9.3.0")]
+441
View File
@@ -0,0 +1,441 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Threading;
namespace OpenMetaverse
{
/// <summary>
/// Class that handles the local asset cache
/// </summary>
public class AssetCache
{
// User can plug in a routine to compute the asset cache location
public delegate string ComputeAssetCacheFilenameDelegate(string cacheDir, UUID assetID);
public ComputeAssetCacheFilenameDelegate ComputeAssetCacheFilename = null;
private GridClient Client;
private Thread cleanerThread;
private System.Timers.Timer cleanerTimer;
private double pruneInterval = 1000 * 60 * 5;
private bool autoPruneEnabled = true;
/// <summary>
/// Allows setting weather to periodicale prune the cache if it grows too big
/// Default is enabled, when caching is enabled
/// </summary>
public bool AutoPruneEnabled
{
set
{
autoPruneEnabled = value;
if (autoPruneEnabled)
{
SetupTimer();
}
else
{
DestroyTimer();
}
}
get { return autoPruneEnabled; }
}
/// <summary>
/// How long (in ms) between cache checks (default is 5 min.)
/// </summary>
public double AutoPruneInterval
{
set
{
pruneInterval = value;
SetupTimer();
}
get { return pruneInterval; }
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="client">A reference to the GridClient object</param>
public AssetCache(GridClient client)
{
Client = client;
Client.Network.LoginProgress += delegate(object sender, LoginProgressEventArgs e)
{
if (e.Status == LoginStatus.Success)
{
SetupTimer();
}
};
Client.Network.Disconnected += delegate(object sender, DisconnectedEventArgs e) { DestroyTimer(); };
}
/// <summary>
/// Disposes cleanup timer
/// </summary>
private void DestroyTimer()
{
if (cleanerTimer != null)
{
cleanerTimer.Dispose();
cleanerTimer = null;
}
}
/// <summary>
/// Only create timer when needed
/// </summary>
private void SetupTimer()
{
if (Operational() && autoPruneEnabled && Client.Network.Connected)
{
if (cleanerTimer == null)
{
cleanerTimer = new System.Timers.Timer(pruneInterval);
cleanerTimer.Elapsed += new System.Timers.ElapsedEventHandler(cleanerTimer_Elapsed);
}
cleanerTimer.Interval = pruneInterval;
cleanerTimer.Enabled = true;
}
}
/// <summary>
/// Return bytes read from the local asset cache, null if it does not exist
/// </summary>
/// <param name="assetID">UUID of the asset we want to get</param>
/// <returns>Raw bytes of the asset, or null on failure</returns>
public byte[] GetCachedAssetBytes(UUID assetID)
{
if (!Operational())
{
return null;
}
try
{
byte[] data;
if (File.Exists(FileName(assetID)))
{
DebugLog("Reading " + FileName(assetID) + " from asset cache.");
data = File.ReadAllBytes(FileName(assetID));
}
else
{
DebugLog("Reading " + StaticFileName(assetID) + " from static asset cache.");
data = File.ReadAllBytes(StaticFileName(assetID));
}
return data;
}
catch (Exception ex)
{
DebugLog("Failed reading asset from cache (" + ex.Message + ")");
return null;
}
}
/// <summary>
/// Returns ImageDownload object of the
/// image from the local image cache, null if it does not exist
/// </summary>
/// <param name="imageID">UUID of the image we want to get</param>
/// <returns>ImageDownload object containing the image, or null on failure</returns>
public ImageDownload GetCachedImage(UUID imageID)
{
if (!Operational())
return null;
byte[] imageData = GetCachedAssetBytes(imageID);
if (imageData == null)
return null;
ImageDownload transfer = new ImageDownload();
transfer.AssetType = AssetType.Texture;
transfer.ID = imageID;
transfer.Simulator = Client.Network.CurrentSim;
transfer.Size = imageData.Length;
transfer.Success = true;
transfer.Transferred = imageData.Length;
transfer.AssetData = imageData;
return transfer;
}
/// <summary>
/// Constructs a file name of the cached asset
/// </summary>
/// <param name="assetID">UUID of the asset</param>
/// <returns>String with the file name of the cahced asset</returns>
private string FileName(UUID assetID)
{
if (ComputeAssetCacheFilename != null)
{
return ComputeAssetCacheFilename(Client.Settings.ASSET_CACHE_DIR, assetID);
}
return Client.Settings.ASSET_CACHE_DIR + Path.DirectorySeparatorChar + assetID.ToString();
}
/// <summary>
/// Constructs a file name of the static cached asset
/// </summary>
/// <param name="assetID">UUID of the asset</param>
/// <returns>String with the file name of the static cached asset</returns>
private string StaticFileName(UUID assetID)
{
return Settings.RESOURCE_DIR + Path.DirectorySeparatorChar + "static_assets" + Path.DirectorySeparatorChar + assetID.ToString();
}
/// <summary>
/// Saves an asset to the local cache
/// </summary>
/// <param name="assetID">UUID of the asset</param>
/// <param name="assetData">Raw bytes the asset consists of</param>
/// <returns>Weather the operation was successfull</returns>
public bool SaveAssetToCache(UUID assetID, byte[] assetData)
{
if (!Operational())
{
return false;
}
try
{
DebugLog("Saving " + FileName(assetID) + " to asset cache.");
if (!Directory.Exists(Client.Settings.ASSET_CACHE_DIR))
{
Directory.CreateDirectory(Client.Settings.ASSET_CACHE_DIR);
}
File.WriteAllBytes(FileName(assetID), assetData);
}
catch (Exception ex)
{
Logger.Log("Failed saving asset to cache (" + ex.Message + ")", Helpers.LogLevel.Warning, Client);
return false;
}
return true;
}
private void DebugLog(string message)
{
if (Client.Settings.LOG_DISKCACHE) Logger.DebugLog(message, Client);
}
/// <summary>
/// Get the file name of the asset stored with gived UUID
/// </summary>
/// <param name="assetID">UUID of the asset</param>
/// <returns>Null if we don't have that UUID cached on disk, file name if found in the cache folder</returns>
public string AssetFileName(UUID assetID)
{
if (!Operational())
{
return null;
}
string fileName = FileName(assetID);
if (File.Exists(fileName))
return fileName;
else
return null;
}
/// <summary>
/// Checks if the asset exists in the local cache
/// </summary>
/// <param name="assetID">UUID of the asset</param>
/// <returns>True is the asset is stored in the cache, otherwise false</returns>
public bool HasAsset(UUID assetID)
{
if (!Operational())
return false;
else
if (File.Exists(FileName(assetID)))
return true;
else
return File.Exists(StaticFileName(assetID));
}
/// <summary>
/// Wipes out entire cache
/// </summary>
public void Clear()
{
string cacheDir = Client.Settings.ASSET_CACHE_DIR;
if (!Directory.Exists(cacheDir))
{
return;
}
DirectoryInfo di = new DirectoryInfo(cacheDir);
// We save file with UUID as file name, only delete those
FileInfo[] files = di.GetFiles("????????-????-????-????-????????????", SearchOption.TopDirectoryOnly);
int num = 0;
foreach (FileInfo file in files)
{
file.Delete();
++num;
}
DebugLog("Wiped out " + num + " files from the cache directory.");
}
/// <summary>
/// Brings cache size to the 90% of the max size
/// </summary>
public void Prune()
{
string cacheDir = Client.Settings.ASSET_CACHE_DIR;
if (!Directory.Exists(cacheDir))
{
return;
}
DirectoryInfo di = new DirectoryInfo(cacheDir);
// We save file with UUID as file name, only count those
FileInfo[] files = di.GetFiles("????????-????-????-????-????????????", SearchOption.TopDirectoryOnly);
long size = GetFileSize(files);
if (size > Client.Settings.ASSET_CACHE_MAX_SIZE)
{
Array.Sort(files, new SortFilesByAccesTimeHelper());
long targetSize = (long)(Client.Settings.ASSET_CACHE_MAX_SIZE * 0.9);
int num = 0;
foreach (FileInfo file in files)
{
++num;
size -= file.Length;
file.Delete();
if (size < targetSize)
{
break;
}
}
DebugLog(num + " files deleted from the cache, cache size now: " + NiceFileSize(size));
}
else
{
DebugLog("Cache size is " + NiceFileSize(size) + ", file deletion not needed");
}
}
/// <summary>
/// Asynchronously brings cache size to the 90% of the max size
/// </summary>
public void BeginPrune()
{
// Check if the background cache cleaning thread is active first
if (cleanerThread != null && cleanerThread.IsAlive)
{
return;
}
lock (this)
{
cleanerThread = new Thread(new ThreadStart(this.Prune));
cleanerThread.IsBackground = true;
cleanerThread.Start();
}
}
/// <summary>
/// Adds up file sizes passes in a FileInfo array
/// </summary>
long GetFileSize(FileInfo[] files)
{
long ret = 0;
foreach (FileInfo file in files)
{
ret += file.Length;
}
return ret;
}
/// <summary>
/// Checks whether caching is enabled
/// </summary>
private bool Operational()
{
return Client.Settings.USE_ASSET_CACHE;
}
/// <summary>
/// Periodically prune the cache
/// </summary>
private void cleanerTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
BeginPrune();
}
/// <summary>
/// Nicely formats file sizes
/// </summary>
/// <param name="byteCount">Byte size we want to output</param>
/// <returns>String with humanly readable file size</returns>
private string NiceFileSize(long byteCount)
{
string size = "0 Bytes";
if (byteCount >= 1073741824)
size = String.Format("{0:##.##}", byteCount / 1073741824) + " GB";
else if (byteCount >= 1048576)
size = String.Format("{0:##.##}", byteCount / 1048576) + " MB";
else if (byteCount >= 1024)
size = String.Format("{0:##.##}", byteCount / 1024) + " KB";
else if (byteCount > 0 && byteCount < 1024)
size = byteCount.ToString() + " Bytes";
return size;
}
/// <summary>
/// Helper class for sorting files by their last accessed time
/// </summary>
private class SortFilesByAccesTimeHelper : IComparer<FileInfo>
{
int IComparer<FileInfo>.Compare(FileInfo f1, FileInfo f2)
{
if (f1.LastAccessTime > f2.LastAccessTime)
return 1;
if (f1.LastAccessTime < f2.LastAccessTime)
return -1;
else
return 0;
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Collections.Generic;
using OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Constants for the archiving module
/// </summary>
public class ArchiveConstants
{
/// <summary>
/// The location of the archive control file
/// </summary>
public static readonly string CONTROL_FILE_PATH = "archive.xml";
/// <summary>
/// Path for the assets held in an archive
/// </summary>
public static readonly string ASSETS_PATH = "assets/";
/// <summary>
/// Path for the prims file
/// </summary>
public static readonly string OBJECTS_PATH = "objects/";
/// <summary>
/// Path for terrains. Technically these may be assets, but I think it's quite nice to split them out.
/// </summary>
public static readonly string TERRAINS_PATH = "terrains/";
/// <summary>
/// Path for region settings.
/// </summary>
public static readonly string SETTINGS_PATH = "settings/";
/// <value>
/// Path for region settings.
/// </value>
public const string LANDDATA_PATH = "landdata/";
/// <summary>
/// The character the separates the uuid from extension information in an archived asset filename
/// </summary>
public static readonly string ASSET_EXTENSION_SEPARATOR = "_";
/// <summary>
/// Extensions used for asset types in the archive
/// </summary>
public static readonly IDictionary<AssetType, string> ASSET_TYPE_TO_EXTENSION = new Dictionary<AssetType, string>();
public static readonly IDictionary<string, AssetType> EXTENSION_TO_ASSET_TYPE = new Dictionary<string, AssetType>();
static ArchiveConstants()
{
ASSET_TYPE_TO_EXTENSION[AssetType.Animation] = ASSET_EXTENSION_SEPARATOR + "animation.bvh";
ASSET_TYPE_TO_EXTENSION[AssetType.Bodypart] = ASSET_EXTENSION_SEPARATOR + "bodypart.txt";
ASSET_TYPE_TO_EXTENSION[AssetType.CallingCard] = ASSET_EXTENSION_SEPARATOR + "callingcard.txt";
ASSET_TYPE_TO_EXTENSION[AssetType.Clothing] = ASSET_EXTENSION_SEPARATOR + "clothing.txt";
ASSET_TYPE_TO_EXTENSION[AssetType.Folder] = ASSET_EXTENSION_SEPARATOR + "folder.txt"; // Not sure if we'll ever see this
ASSET_TYPE_TO_EXTENSION[AssetType.Gesture] = ASSET_EXTENSION_SEPARATOR + "gesture.txt";
ASSET_TYPE_TO_EXTENSION[AssetType.ImageJPEG] = ASSET_EXTENSION_SEPARATOR + "image.jpg";
ASSET_TYPE_TO_EXTENSION[AssetType.ImageTGA] = ASSET_EXTENSION_SEPARATOR + "image.tga";
ASSET_TYPE_TO_EXTENSION[AssetType.Landmark] = ASSET_EXTENSION_SEPARATOR + "landmark.txt";
ASSET_TYPE_TO_EXTENSION[AssetType.LostAndFoundFolder] = ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"; // Not sure if we'll ever see this
ASSET_TYPE_TO_EXTENSION[AssetType.LSLBytecode] = ASSET_EXTENSION_SEPARATOR + "bytecode.lso";
ASSET_TYPE_TO_EXTENSION[AssetType.LSLText] = ASSET_EXTENSION_SEPARATOR + "script.lsl";
ASSET_TYPE_TO_EXTENSION[AssetType.Notecard] = ASSET_EXTENSION_SEPARATOR + "notecard.txt";
ASSET_TYPE_TO_EXTENSION[AssetType.Object] = ASSET_EXTENSION_SEPARATOR + "object.xml";
ASSET_TYPE_TO_EXTENSION[AssetType.RootFolder] = ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"; // Not sure if we'll ever see this
ASSET_TYPE_TO_EXTENSION[AssetType.Simstate] = ASSET_EXTENSION_SEPARATOR + "simstate.bin"; // Not sure if we'll ever see this
ASSET_TYPE_TO_EXTENSION[AssetType.SnapshotFolder] = ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"; // Not sure if we'll ever see this
ASSET_TYPE_TO_EXTENSION[AssetType.Sound] = ASSET_EXTENSION_SEPARATOR + "sound.ogg";
ASSET_TYPE_TO_EXTENSION[AssetType.SoundWAV] = ASSET_EXTENSION_SEPARATOR + "sound.wav";
ASSET_TYPE_TO_EXTENSION[AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2";
ASSET_TYPE_TO_EXTENSION[AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga";
ASSET_TYPE_TO_EXTENSION[AssetType.TrashFolder] = ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"; // Not sure if we'll ever see this
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = AssetType.Animation;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = AssetType.Bodypart;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "callingcard.txt"] = AssetType.CallingCard;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "clothing.txt"] = AssetType.Clothing;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "folder.txt"] = AssetType.Folder;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "gesture.txt"] = AssetType.Gesture;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.jpg"] = AssetType.ImageJPEG;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.tga"] = AssetType.ImageTGA;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "landmark.txt"] = AssetType.Landmark;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"] = AssetType.LostAndFoundFolder;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bytecode.lso"] = AssetType.LSLBytecode;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "script.lsl"] = AssetType.LSLText;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "notecard.txt"] = AssetType.Notecard;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "object.xml"] = AssetType.Object;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"] = AssetType.RootFolder;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "simstate.bin"] = AssetType.Simstate;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"] = AssetType.SnapshotFolder;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.ogg"] = AssetType.Sound;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.wav"] = AssetType.SoundWAV;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = AssetType.Texture;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = AssetType.TextureTGA;
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = AssetType.TrashFolder;
}
}
}
@@ -0,0 +1,149 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Reflection;
using System.Xml;
using OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Archives assets
/// </summary>
public class AssetsArchiver
{
///// <value>
///// Post a message to the log every x assets as a progress bar
///// </value>
//static int LOG_ASSET_LOAD_NOTIFICATION_INTERVAL = 50;
/// <summary>
/// Archive assets
/// </summary>
protected IDictionary<UUID, Asset> m_assets;
public AssetsArchiver(IDictionary<UUID, Asset> assets)
{
m_assets = assets;
}
/// <summary>
/// Archive the assets given to this archiver to the given archive.
/// </summary>
/// <param name="archive"></param>
public void Archive(TarArchiveWriter archive)
{
//WriteMetadata(archive);
WriteData(archive);
}
/// <summary>
/// Write an assets metadata file to the given archive
/// </summary>
/// <param name="archive"></param>
protected void WriteMetadata(TarArchiveWriter archive)
{
StringWriter sw = new StringWriter();
using (XmlTextWriter xtw = new XmlTextWriter(sw))
{
xtw.Formatting = Formatting.Indented;
xtw.WriteStartDocument();
xtw.WriteStartElement("assets");
foreach (UUID uuid in m_assets.Keys)
{
Asset asset = m_assets[uuid];
if (asset != null)
{
xtw.WriteStartElement("asset");
string extension = string.Empty;
if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.AssetType))
{
extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.AssetType];
}
xtw.WriteElementString("filename", uuid.ToString() + extension);
xtw.WriteElementString("name", uuid.ToString());
xtw.WriteElementString("description", String.Empty);
xtw.WriteElementString("asset-type", asset.AssetType.ToString());
xtw.WriteEndElement();
}
}
xtw.WriteEndElement();
xtw.WriteEndDocument();
archive.WriteFile("assets.xml", sw.ToString());
}
}
/// <summary>
/// Write asset data files to the given archive
/// </summary>
/// <param name="archive"></param>
protected void WriteData(TarArchiveWriter archive)
{
// It appears that gtar, at least, doesn't need the intermediate directory entries in the tar
//archive.AddDir("assets");
int assetsAdded = 0;
foreach (UUID uuid in m_assets.Keys)
{
Asset asset = m_assets[uuid];
string extension = string.Empty;
if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.AssetType))
{
extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.AssetType];
}
else
{
Logger.Log(String.Format(
"Unrecognized asset type {0} with uuid {1}. This asset will be saved but not reloaded",
asset.AssetType, asset.AssetID), Helpers.LogLevel.Warning);
}
asset.Encode();
archive.WriteFile(
ArchiveConstants.ASSETS_PATH + uuid.ToString() + extension,
asset.AssetData);
assetsAdded++;
}
}
}
}
+879
View File
@@ -0,0 +1,879 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.IO.Compression;
using System.Text;
using System.Xml;
using System.Threading;
using OpenMetaverse;
namespace OpenMetaverse.Assets
{
public static class OarFile
{
public delegate void AssetLoadedCallback(Asset asset, long bytesRead, long totalBytes);
public delegate void TerrainLoadedCallback(float[,] terrain, long bytesRead, long totalBytes);
public delegate void SceneObjectLoadedCallback(AssetPrim linkset, long bytesRead, long totalBytes);
public delegate void SettingsLoadedCallback(string regionName, RegionSettings settings);
#region Archive Loading
public static void UnpackageArchive(string filename, AssetLoadedCallback assetCallback, TerrainLoadedCallback terrainCallback,
SceneObjectLoadedCallback objectCallback, SettingsLoadedCallback settingsCallback)
{
int successfulAssetRestores = 0;
int failedAssetRestores = 0;
try
{
using (FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
using (GZipStream loadStream = new GZipStream(fileStream, CompressionMode.Decompress))
{
TarArchiveReader archive = new TarArchiveReader(loadStream);
string filePath;
byte[] data;
TarArchiveReader.TarEntryType entryType;
while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
{
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
{
// Deserialize the XML bytes
if (objectCallback != null)
LoadObjects(data, objectCallback, fileStream.Position, fileStream.Length);
}
else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
{
if (assetCallback != null)
{
if (LoadAsset(filePath, data, assetCallback, fileStream.Position, fileStream.Length))
successfulAssetRestores++;
else
failedAssetRestores++;
}
}
else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
{
if (terrainCallback != null)
LoadTerrain(filePath, data, terrainCallback, fileStream.Position, fileStream.Length);
}
else if (filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
{
if (settingsCallback != null)
LoadRegionSettings(filePath, data, settingsCallback);
}
}
archive.Close();
}
}
}
catch (Exception e)
{
Logger.Log("[OarFile] Error loading OAR file: " + e.Message, Helpers.LogLevel.Error);
return;
}
if (failedAssetRestores > 0)
Logger.Log(String.Format("[OarFile]: Failed to load {0} assets", failedAssetRestores), Helpers.LogLevel.Warning);
}
private static bool LoadAsset(string assetPath, byte[] data, AssetLoadedCallback assetCallback, long bytesRead, long totalBytes)
{
// Right now we're nastily obtaining the UUID from the filename
string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
if (i == -1)
{
Logger.Log(String.Format(
"[OarFile]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping",
assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR), Helpers.LogLevel.Warning);
return false;
}
string extension = filename.Substring(i);
UUID uuid;
UUID.TryParse(filename.Remove(filename.Length - extension.Length), out uuid);
if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension))
{
AssetType assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension];
Asset asset = null;
switch (assetType)
{
case AssetType.Animation:
asset = new AssetAnimation(uuid, data);
break;
case AssetType.Bodypart:
asset = new AssetBodypart(uuid, data);
break;
case AssetType.Clothing:
asset = new AssetClothing(uuid, data);
break;
case AssetType.Gesture:
asset = new AssetGesture(uuid, data);
break;
case AssetType.Landmark:
asset = new AssetLandmark(uuid, data);
break;
case AssetType.LSLBytecode:
asset = new AssetScriptBinary(uuid, data);
break;
case AssetType.LSLText:
asset = new AssetScriptText(uuid, data);
break;
case AssetType.Notecard:
asset = new AssetNotecard(uuid, data);
break;
case AssetType.Object:
asset = new AssetPrim(uuid, data);
break;
case AssetType.Sound:
asset = new AssetSound(uuid, data);
break;
case AssetType.Texture:
asset = new AssetTexture(uuid, data);
break;
default:
Logger.Log("[OarFile] Unhandled asset type " + assetType, Helpers.LogLevel.Error);
break;
}
if (asset != null)
{
assetCallback(asset, bytesRead, totalBytes);
return true;
}
}
Logger.Log("[OarFile] Failed to load asset", Helpers.LogLevel.Warning);
return false;
}
private static bool LoadRegionSettings(string filePath, byte[] data, SettingsLoadedCallback settingsCallback)
{
RegionSettings settings = null;
bool loaded = false;
try
{
using (MemoryStream stream = new MemoryStream(data))
settings = RegionSettings.FromStream(stream);
loaded = true;
}
catch (Exception ex)
{
Logger.Log("[OarFile] Failed to parse region settings file " + filePath + ": " + ex.Message, Helpers.LogLevel.Warning);
}
// Parse the region name out of the filename
string regionName = Path.GetFileNameWithoutExtension(filePath);
if (loaded)
settingsCallback(regionName, settings);
return loaded;
}
private static bool LoadTerrain(string filePath, byte[] data, TerrainLoadedCallback terrainCallback, long bytesRead, long totalBytes)
{
float[,] terrain = new float[256, 256];
bool loaded = false;
switch (Path.GetExtension(filePath))
{
case ".r32":
case ".f32":
// RAW32
if (data.Length == 256 * 256 * 4)
{
int pos = 0;
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
terrain[y, x] = Utils.Clamp(Utils.BytesToFloat(data, pos), 0.0f, 255.0f);
pos += 4;
}
}
loaded = true;
}
else
{
Logger.Log("[OarFile] RAW32 terrain file " + filePath + " has the wrong number of bytes: " + data.Length,
Helpers.LogLevel.Warning);
}
break;
case ".ter":
// Terragen
case ".raw":
// LLRAW
case ".jpg":
case ".jpeg":
// JPG
case ".bmp":
// BMP
case ".png":
// PNG
case ".gif":
// GIF
case ".tif":
case ".tiff":
// TIFF
default:
Logger.Log("[OarFile] Unrecognized terrain format in " + filePath, Helpers.LogLevel.Warning);
break;
}
if (loaded)
terrainCallback(terrain, bytesRead, totalBytes);
return loaded;
}
public static void LoadObjects(byte[] objectData, SceneObjectLoadedCallback objectCallback, long bytesRead, long totalBytes)
{
XmlDocument doc = new XmlDocument();
using (XmlTextReader reader = new XmlTextReader(new MemoryStream(objectData)))
{
reader.WhitespaceHandling = WhitespaceHandling.None;
doc.Load(reader);
}
XmlNode rootNode = doc.FirstChild;
if (rootNode.LocalName.Equals("scene"))
{
foreach (XmlNode node in rootNode.ChildNodes)
{
AssetPrim linkset = new AssetPrim(node.OuterXml);
if (linkset != null)
objectCallback(linkset, bytesRead, totalBytes);
}
}
else
{
AssetPrim linkset = new AssetPrim(rootNode.OuterXml);
if (linkset != null)
objectCallback(linkset, bytesRead, totalBytes);
}
}
#endregion Archive Loading
#region Archive Saving
public static void PackageArchive(string directoryName, string filename)
{
const string ARCHIVE_XML = "<?xml version=\"1.0\" encoding=\"utf-16\"?>\n<archive major_version=\"0\" minor_version=\"1\" />";
TarArchiveWriter archive = new TarArchiveWriter(new GZipStream(new FileStream(filename, FileMode.Create), CompressionMode.Compress));
// Create the archive.xml file
archive.WriteFile("archive.xml", ARCHIVE_XML);
// Add the assets
string[] files = Directory.GetFiles(directoryName + "/" + ArchiveConstants.ASSETS_PATH);
foreach (string file in files)
archive.WriteFile(ArchiveConstants.ASSETS_PATH + Path.GetFileName(file), File.ReadAllBytes(file));
// Add the objects
files = Directory.GetFiles(directoryName + "/" + ArchiveConstants.OBJECTS_PATH);
foreach (string file in files)
archive.WriteFile(ArchiveConstants.OBJECTS_PATH + Path.GetFileName(file), File.ReadAllBytes(file));
// Add the terrain(s)
files = Directory.GetFiles(directoryName + "/" + ArchiveConstants.TERRAINS_PATH);
foreach (string file in files)
archive.WriteFile(ArchiveConstants.TERRAINS_PATH + Path.GetFileName(file), File.ReadAllBytes(file));
// Add the parcels(s)
files = Directory.GetFiles(directoryName + "/" + ArchiveConstants.LANDDATA_PATH);
foreach (string file in files)
archive.WriteFile(ArchiveConstants.LANDDATA_PATH + Path.GetFileName(file), File.ReadAllBytes(file));
// Add the setting(s)
files = Directory.GetFiles(directoryName + "/" + ArchiveConstants.SETTINGS_PATH);
foreach (string file in files)
archive.WriteFile(ArchiveConstants.SETTINGS_PATH + Path.GetFileName(file), File.ReadAllBytes(file));
archive.Close();
}
public static void SaveTerrain(Simulator sim, string terrainPath)
{
if (Directory.Exists(terrainPath))
Directory.Delete(terrainPath, true);
Thread.Sleep(100);
Directory.CreateDirectory(terrainPath);
Thread.Sleep(100);
FileInfo file = new FileInfo(Path.Combine(terrainPath, sim.Name + ".r32"));
FileStream s = file.Open(FileMode.Create, FileAccess.Write);
SaveTerrainStream(s, sim);
s.Close();
}
private static void SaveTerrainStream(Stream s, Simulator sim)
{
BinaryWriter bs = new BinaryWriter(s);
int y;
for (y = 0; y < 256; y++)
{
int x;
for (x = 0; x < 256; x++)
{
float height;
sim.TerrainHeightAtPoint(x, y, out height);
bs.Write(height);
}
}
bs.Close();
}
public static void SaveParcels(Simulator sim, string parcelPath)
{
if (Directory.Exists(parcelPath))
Directory.Delete(parcelPath, true);
Thread.Sleep(100);
Directory.CreateDirectory(parcelPath);
Thread.Sleep(100);
sim.Parcels.ForEach((Parcel parcel) =>
{
UUID globalID = UUID.Random();
SerializeParcel(parcel, globalID, Path.Combine(parcelPath, globalID + ".xml"));
});
}
private static void SerializeParcel(Parcel parcel, UUID globalID, string filename)
{
StringWriter sw = new StringWriter();
XmlTextWriter xtw = new XmlTextWriter(sw) { Formatting = Formatting.Indented };
xtw.WriteStartDocument();
xtw.WriteStartElement("LandData");
xtw.WriteElementString("Area", Convert.ToString(parcel.Area));
xtw.WriteElementString("AuctionID", Convert.ToString(parcel.AuctionID));
xtw.WriteElementString("AuthBuyerID", parcel.AuthBuyerID.ToString());
xtw.WriteElementString("Category", Convert.ToString((sbyte)parcel.Category));
TimeSpan t = parcel.ClaimDate.ToUniversalTime() - Utils.Epoch;
xtw.WriteElementString("ClaimDate", Convert.ToString((int)t.TotalSeconds));
xtw.WriteElementString("ClaimPrice", Convert.ToString(parcel.ClaimPrice));
xtw.WriteElementString("GlobalID", globalID.ToString());
xtw.WriteElementString("GroupID", parcel.GroupID.ToString());
xtw.WriteElementString("IsGroupOwned", Convert.ToString(parcel.IsGroupOwned));
xtw.WriteElementString("Bitmap", Convert.ToBase64String(parcel.Bitmap));
xtw.WriteElementString("Description", parcel.Desc);
xtw.WriteElementString("Flags", Convert.ToString((uint)parcel.Flags));
xtw.WriteElementString("LandingType", Convert.ToString((byte)parcel.Landing));
xtw.WriteElementString("Name", parcel.Name);
xtw.WriteElementString("Status", Convert.ToString((sbyte)parcel.Status));
xtw.WriteElementString("LocalID", parcel.LocalID.ToString());
xtw.WriteElementString("MediaAutoScale", Convert.ToString(parcel.Media.MediaAutoScale ? 1 : 0));
xtw.WriteElementString("MediaID", parcel.Media.MediaID.ToString());
xtw.WriteElementString("MediaURL", parcel.Media.MediaURL);
xtw.WriteElementString("MusicURL", parcel.MusicURL);
xtw.WriteElementString("OwnerID", parcel.OwnerID.ToString());
xtw.WriteStartElement("ParcelAccessList");
foreach (ParcelManager.ParcelAccessEntry pal in parcel.AccessBlackList)
{
xtw.WriteStartElement("ParcelAccessEntry");
xtw.WriteElementString("AgentID", pal.AgentID.ToString());
xtw.WriteElementString("Time", pal.Time.ToString("s"));
xtw.WriteElementString("AccessList", Convert.ToString((uint)pal.Flags));
xtw.WriteEndElement();
}
foreach (ParcelManager.ParcelAccessEntry pal in parcel.AccessWhiteList)
{
xtw.WriteStartElement("ParcelAccessEntry");
xtw.WriteElementString("AgentID", pal.AgentID.ToString());
xtw.WriteElementString("Time", pal.Time.ToString("s"));
xtw.WriteElementString("AccessList", Convert.ToString((uint)pal.Flags));
xtw.WriteEndElement();
}
xtw.WriteEndElement();
xtw.WriteElementString("PassHours", Convert.ToString(parcel.PassHours));
xtw.WriteElementString("PassPrice", Convert.ToString(parcel.PassPrice));
xtw.WriteElementString("SalePrice", Convert.ToString(parcel.SalePrice));
xtw.WriteElementString("SnapshotID", parcel.SnapshotID.ToString());
xtw.WriteElementString("UserLocation", parcel.UserLocation.ToString());
xtw.WriteElementString("UserLookAt", parcel.UserLookAt.ToString());
xtw.WriteElementString("Dwell", "0");
xtw.WriteElementString("OtherCleanTime", Convert.ToString(parcel.OtherCleanTime));
xtw.WriteEndElement();
xtw.Close();
sw.Close();
File.WriteAllText(filename, sw.ToString());
}
public static void SaveRegionSettings(Simulator sim, string settingsPath)
{
if (Directory.Exists(settingsPath))
Directory.Delete(settingsPath, true);
Thread.Sleep(100);
Directory.CreateDirectory(settingsPath);
Thread.Sleep(100);
RegionSettings settings = new RegionSettings();
//settings.AgentLimit;
settings.AllowDamage = (sim.Flags & RegionFlags.AllowDamage) == RegionFlags.AllowDamage;
//settings.AllowLandJoinDivide;
settings.AllowLandResell = (sim.Flags & RegionFlags.BlockLandResell) != RegionFlags.BlockLandResell;
settings.BlockFly = (sim.Flags & RegionFlags.NoFly) == RegionFlags.NoFly;
settings.BlockLandShowInSearch = (sim.Flags & RegionFlags.BlockParcelSearch) == RegionFlags.BlockParcelSearch;
settings.BlockTerraform = (sim.Flags & RegionFlags.BlockTerraform) == RegionFlags.BlockTerraform;
settings.DisableCollisions = (sim.Flags & RegionFlags.SkipCollisions) == RegionFlags.SkipCollisions;
settings.DisablePhysics = (sim.Flags & RegionFlags.SkipPhysics) == RegionFlags.SkipPhysics;
settings.DisableScripts = (sim.Flags & RegionFlags.SkipScripts) == RegionFlags.SkipScripts;
settings.FixedSun = (sim.Flags & RegionFlags.SunFixed) == RegionFlags.SunFixed;
settings.MaturityRating = (int)(sim.Access & SimAccess.Mature & SimAccess.Adult & SimAccess.PG);
//settings.ObjectBonus;
settings.RestrictPushing = (sim.Flags & RegionFlags.RestrictPushObject) == RegionFlags.RestrictPushObject;
settings.TerrainDetail0 = sim.TerrainDetail0;
settings.TerrainDetail1 = sim.TerrainDetail1;
settings.TerrainDetail2 = sim.TerrainDetail2;
settings.TerrainDetail3 = sim.TerrainDetail3;
settings.TerrainHeightRange00 = sim.TerrainHeightRange00;
settings.TerrainHeightRange01 = sim.TerrainHeightRange01;
settings.TerrainHeightRange10 = sim.TerrainHeightRange10;
settings.TerrainHeightRange11 = sim.TerrainHeightRange11;
settings.TerrainStartHeight00 = sim.TerrainStartHeight00;
settings.TerrainStartHeight01 = sim.TerrainStartHeight01;
settings.TerrainStartHeight10 = sim.TerrainStartHeight10;
settings.TerrainStartHeight11 = sim.TerrainStartHeight11;
//settings.UseEstateSun;
settings.WaterHeight = sim.WaterHeight;
settings.ToXML(Path.Combine(settingsPath, sim.Name + ".xml"));
}
public static void SavePrims(AssetManager manager, IList<AssetPrim> prims, string primsPath, string assetsPath)
{
Dictionary<UUID, UUID> textureList = new Dictionary<UUID, UUID>();
// Delete all of the old linkset files
try { Directory.Delete(primsPath, true); }
catch (Exception) { }
Thread.Sleep(100);
// Create a new folder for the linkset files
try { Directory.CreateDirectory(primsPath); }
catch (Exception ex)
{
Logger.Log("Failed saving prims: " + ex.Message, Helpers.LogLevel.Error);
return;
}
Thread.Sleep(100);
try
{
foreach (AssetPrim assetPrim in prims)
{
SavePrim(assetPrim, Path.Combine(primsPath, "Primitive_" + assetPrim.Parent.ID + ".xml"));
CollectTextures(assetPrim.Parent, textureList);
if (assetPrim.Children != null)
{
foreach (PrimObject child in assetPrim.Children)
CollectTextures(child, textureList);
}
}
SaveAssets(manager, AssetType.Texture, new List<UUID>(textureList.Keys), assetsPath);
}
catch
{
}
}
static void CollectTextures(PrimObject prim, Dictionary<UUID, UUID> textureList)
{
if (prim.Textures != null)
{
// Add all of the textures on this prim to the save list
if (prim.Textures.DefaultTexture != null)
textureList[prim.Textures.DefaultTexture.TextureID] = prim.Textures.DefaultTexture.TextureID;
if (prim.Textures.FaceTextures != null)
{
for (int i = 0; i < prim.Textures.FaceTextures.Length; i++)
{
Primitive.TextureEntryFace face = prim.Textures.FaceTextures[i];
if (face != null)
textureList[face.TextureID] = face.TextureID;
}
}
if(prim.Sculpt != null && prim.Sculpt.Texture != UUID.Zero)
textureList[prim.Sculpt.Texture] = prim.Sculpt.Texture;
}
}
public static void ClearAssetFolder(string assetsPath)
{
// Delete the assets folder
try { Directory.Delete(assetsPath, true); }
catch (Exception) { }
Thread.Sleep(100);
// Create a new assets folder
try { Directory.CreateDirectory(assetsPath); }
catch (Exception ex)
{
Logger.Log("Failed saving assets: " + ex.Message, Helpers.LogLevel.Error);
return;
}
Thread.Sleep(100);
}
public static void SaveAssets(AssetManager assetManager, AssetType assetType, IList<UUID> assets, string assetsPath)
{
int count = 0;
List<UUID> remainingTextures = new List<UUID>(assets);
AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false);
for (int i = 0; i < assets.Count; i++)
{
UUID texture = assets[i];
if(assetType == AssetType.Texture)
{
assetManager.RequestImage(texture, (state, assetTexture) =>
{
string extension = string.Empty;
if (assetTexture == null)
{
Console.WriteLine("Missing asset " + texture);
return;
}
if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(assetType))
extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[assetType];
File.WriteAllBytes(Path.Combine(assetsPath, texture.ToString() + extension), assetTexture.AssetData);
remainingTextures.Remove(assetTexture.AssetID);
if (remainingTextures.Count == 0)
AllPropertiesReceived.Set();
++count;
});
}
else
{
assetManager.RequestAsset(texture, assetType, false, (transfer, asset) =>
{
string extension = string.Empty;
if (asset == null)
{
Console.WriteLine("Missing asset " + texture);
return;
}
if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(assetType))
extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[assetType];
File.WriteAllBytes(Path.Combine(assetsPath, texture.ToString() + extension), asset.AssetData);
remainingTextures.Remove(asset.AssetID);
if (remainingTextures.Count == 0)
AllPropertiesReceived.Set();
++count;
});
}
Thread.Sleep(200);
if (i % 5 == 0)
Thread.Sleep(250);
}
AllPropertiesReceived.WaitOne(5000 + 350 * assets.Count);
Logger.Log("Copied " + count + " textures to the asset archive folder", Helpers.LogLevel.Info);
}
public static void SaveSimAssets(AssetManager assetManager, AssetType assetType, UUID assetID, UUID itemID, UUID primID, string assetsPath)
{
int count = 0;
AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false);
assetManager.RequestAsset(assetID, itemID, primID, assetType, false, SourceType.SimInventoryItem, UUID.Random(), (transfer, asset) =>
{
string extension = string.Empty;
if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(assetType))
extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[assetType];
if (asset == null)
{
AllPropertiesReceived.Set();
return;
}
File.WriteAllBytes(Path.Combine(assetsPath, assetID.ToString() + extension), asset.AssetData);
++count;
AllPropertiesReceived.Set();
});
AllPropertiesReceived.WaitOne(5000);
Logger.Log("Copied " + count + " textures to the asset archive folder", Helpers.LogLevel.Info);
}
static void SavePrim(AssetPrim prim, string filename)
{
try
{
using (StreamWriter stream = new StreamWriter(filename))
{
XmlTextWriter writer = new XmlTextWriter(stream);
writer.Formatting = Formatting.Indented;
writer.Indentation = 4;
writer.IndentChar = ' ';
SOGToXml2(writer, prim);
writer.Flush();
}
}
catch (Exception ex)
{
Logger.Log("Failed saving linkset: " + ex.Message, Helpers.LogLevel.Error);
}
}
public static void SOGToXml2(XmlTextWriter writer, AssetPrim prim)
{
writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty);
SOPToXml(writer, prim.Parent, null);
writer.WriteStartElement(String.Empty, "OtherParts", String.Empty);
foreach (PrimObject child in prim.Children)
SOPToXml(writer, child, prim.Parent);
writer.WriteEndElement();
writer.WriteEndElement();
}
static void SOPToXml(XmlTextWriter writer, PrimObject prim, PrimObject parent)
{
writer.WriteStartElement("SceneObjectPart");
writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteAttributeString("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
WriteUUID(writer, "CreatorID", prim.CreatorID);
WriteUUID(writer, "FolderID", prim.FolderID);
writer.WriteElementString("InventorySerial", (prim.Inventory != null) ? prim.Inventory.Serial.ToString() : "0");
// FIXME: Task inventory
writer.WriteStartElement("TaskInventory");
if (prim.Inventory != null)
{
foreach (PrimObject.InventoryBlock.ItemBlock item in prim.Inventory.Items)
{
writer.WriteStartElement("", "TaskInventoryItem", "");
WriteUUID(writer, "AssetID", item.AssetID);
writer.WriteElementString("BasePermissions", item.PermsBase.ToString());
writer.WriteElementString("CreationDate", (item.CreationDate.ToUniversalTime() - Utils.Epoch).TotalSeconds.ToString());
WriteUUID(writer, "CreatorID", item.CreatorID);
writer.WriteElementString("Description", item.Description);
writer.WriteElementString("EveryonePermissions", item.PermsEveryone.ToString());
writer.WriteElementString("Flags", item.Flags.ToString());
WriteUUID(writer, "GroupID", item.GroupID);
writer.WriteElementString("GroupPermissions", item.PermsGroup.ToString());
writer.WriteElementString("InvType", ((int)item.InvType).ToString());
WriteUUID(writer, "ItemID", item.ID);
WriteUUID(writer, "OldItemID", UUID.Zero);
WriteUUID(writer, "LastOwnerID", item.LastOwnerID);
writer.WriteElementString("Name", item.Name);
writer.WriteElementString("NextPermissions", item.PermsNextOwner.ToString());
WriteUUID(writer, "OwnerID", item.OwnerID);
writer.WriteElementString("CurrentPermissions", item.PermsOwner.ToString());
WriteUUID(writer, "ParentID", prim.ID);
WriteUUID(writer, "ParentPartID", prim.ID);
WriteUUID(writer, "PermsGranter", item.PermsGranterID);
writer.WriteElementString("PermsMask", "0");
writer.WriteElementString("Type", ((int)item.Type).ToString());
writer.WriteElementString("OwnerChanged", "false");
writer.WriteEndElement();
}
}
writer.WriteEndElement();
PrimFlags flags = PrimFlags.None;
if (prim.UsePhysics) flags |= PrimFlags.Physics;
if (prim.Phantom) flags |= PrimFlags.Phantom;
if (prim.DieAtEdge) flags |= PrimFlags.DieAtEdge;
if (prim.ReturnAtEdge) flags |= PrimFlags.ReturnAtEdge;
if (prim.Temporary) flags |= PrimFlags.Temporary;
if (prim.Sandbox) flags |= PrimFlags.Sandbox;
writer.WriteElementString("ObjectFlags", ((int)flags).ToString());
WriteUUID(writer, "UUID", prim.ID);
writer.WriteElementString("LocalId", prim.LocalID.ToString());
writer.WriteElementString("Name", prim.Name);
writer.WriteElementString("Material", ((int)prim.Material).ToString());
writer.WriteElementString("RegionHandle", prim.RegionHandle.ToString());
writer.WriteElementString("ScriptAccessPin", prim.RemoteScriptAccessPIN.ToString());
Vector3 groupPosition;
if (parent == null)
groupPosition = prim.Position;
else
groupPosition = parent.Position;
WriteVector(writer, "GroupPosition", groupPosition);
if (prim.ParentID == 0)
WriteVector(writer, "OffsetPosition", Vector3.Zero);
else
WriteVector(writer, "OffsetPosition", prim.Position);
WriteQuaternion(writer, "RotationOffset", prim.Rotation);
WriteVector(writer, "Velocity", prim.Velocity);
WriteVector(writer, "RotationalVelocity", Vector3.Zero);
WriteVector(writer, "AngularVelocity", prim.AngularVelocity);
WriteVector(writer, "Acceleration", prim.Acceleration);
writer.WriteElementString("Description", prim.Description);
writer.WriteStartElement("Color");
writer.WriteElementString("R", prim.TextColor.R.ToString(Utils.EnUsCulture));
writer.WriteElementString("G", prim.TextColor.G.ToString(Utils.EnUsCulture));
writer.WriteElementString("B", prim.TextColor.B.ToString(Utils.EnUsCulture));
writer.WriteElementString("A", prim.TextColor.G.ToString(Utils.EnUsCulture));
writer.WriteEndElement();
writer.WriteElementString("Text", prim.Text);
writer.WriteElementString("SitName", prim.SitName);
writer.WriteElementString("TouchName", prim.TouchName);
writer.WriteElementString("LinkNum", prim.LinkNumber.ToString());
writer.WriteElementString("ClickAction", prim.ClickAction.ToString());
writer.WriteStartElement("Shape");
writer.WriteElementString("PathBegin", Primitive.PackBeginCut(prim.Shape.PathBegin).ToString());
writer.WriteElementString("PathCurve", prim.Shape.PathCurve.ToString());
writer.WriteElementString("PathEnd", Primitive.PackEndCut(prim.Shape.PathEnd).ToString());
writer.WriteElementString("PathRadiusOffset", Primitive.PackPathTwist(prim.Shape.PathRadiusOffset).ToString());
writer.WriteElementString("PathRevolutions", Primitive.PackPathRevolutions(prim.Shape.PathRevolutions).ToString());
writer.WriteElementString("PathScaleX", Primitive.PackPathScale(prim.Shape.PathScaleX).ToString());
writer.WriteElementString("PathScaleY", Primitive.PackPathScale(prim.Shape.PathScaleY).ToString());
writer.WriteElementString("PathShearX", ((byte)Primitive.PackPathShear(prim.Shape.PathShearX)).ToString());
writer.WriteElementString("PathShearY", ((byte)Primitive.PackPathShear(prim.Shape.PathShearY)).ToString());
writer.WriteElementString("PathSkew", Primitive.PackPathTwist(prim.Shape.PathSkew).ToString());
writer.WriteElementString("PathTaperX", Primitive.PackPathTaper(prim.Shape.PathTaperX).ToString());
writer.WriteElementString("PathTaperY", Primitive.PackPathTaper(prim.Shape.PathTaperY).ToString());
writer.WriteElementString("PathTwist", Primitive.PackPathTwist(prim.Shape.PathTwist).ToString());
writer.WriteElementString("PathTwistBegin", Primitive.PackPathTwist(prim.Shape.PathTwistBegin).ToString());
writer.WriteElementString("PCode", prim.PCode.ToString());
writer.WriteElementString("ProfileBegin", Primitive.PackBeginCut(prim.Shape.ProfileBegin).ToString());
writer.WriteElementString("ProfileEnd", Primitive.PackEndCut(prim.Shape.ProfileEnd).ToString());
writer.WriteElementString("ProfileHollow", Primitive.PackProfileHollow(prim.Shape.ProfileHollow).ToString());
WriteVector(writer, "Scale", prim.Scale);
writer.WriteElementString("State", prim.State.ToString());
AssetPrim.ProfileShape shape = (AssetPrim.ProfileShape)(prim.Shape.ProfileCurve & 0x0F);
HoleType hole = (HoleType)(prim.Shape.ProfileCurve & 0xF0);
writer.WriteElementString("ProfileShape", shape.ToString());
writer.WriteElementString("HollowShape", hole.ToString());
writer.WriteElementString("ProfileCurve", prim.Shape.ProfileCurve.ToString());
writer.WriteStartElement("TextureEntry");
byte[] te;
if (prim.Textures != null)
te = prim.Textures.GetBytes();
else
te = Utils.EmptyBytes;
writer.WriteBase64(te, 0, te.Length);
writer.WriteEndElement();
// FIXME: ExtraParams
writer.WriteStartElement("ExtraParams"); writer.WriteEndElement();
writer.WriteEndElement();
WriteVector(writer, "Scale", prim.Scale);
writer.WriteElementString("UpdateFlag", "0");
WriteVector(writer, "SitTargetOrientation", Vector3.UnitZ); // TODO: Is this really a vector and not a quaternion?
WriteVector(writer, "SitTargetPosition", prim.SitOffset);
WriteVector(writer, "SitTargetPositionLL", prim.SitOffset);
WriteQuaternion(writer, "SitTargetOrientationLL", prim.SitRotation);
writer.WriteElementString("ParentID", prim.ParentID.ToString());
writer.WriteElementString("CreationDate", ((int)Utils.DateTimeToUnixTime(prim.CreationDate)).ToString());
writer.WriteElementString("Category", "0");
writer.WriteElementString("SalePrice", prim.SalePrice.ToString());
writer.WriteElementString("ObjectSaleType", ((int)prim.SaleType).ToString());
writer.WriteElementString("OwnershipCost", "0");
WriteUUID(writer, "GroupID", prim.GroupID);
WriteUUID(writer, "OwnerID", prim.OwnerID);
WriteUUID(writer, "LastOwnerID", prim.LastOwnerID);
writer.WriteElementString("BaseMask", ((uint)PermissionMask.All).ToString());
writer.WriteElementString("OwnerMask", ((uint)PermissionMask.All).ToString());
writer.WriteElementString("GroupMask", ((uint)PermissionMask.All).ToString());
writer.WriteElementString("EveryoneMask", ((uint)PermissionMask.All).ToString());
writer.WriteElementString("NextOwnerMask", ((uint)PermissionMask.All).ToString());
writer.WriteElementString("Flags", "None");
WriteUUID(writer, "SitTargetAvatar", UUID.Zero);
writer.WriteEndElement();
}
static void WriteUUID(XmlTextWriter writer, string name, UUID id)
{
writer.WriteStartElement(name);
writer.WriteElementString("UUID", id.ToString());
writer.WriteEndElement();
}
static void WriteVector(XmlTextWriter writer, string name, Vector3 vec)
{
writer.WriteStartElement(name);
writer.WriteElementString("X", vec.X.ToString(Utils.EnUsCulture));
writer.WriteElementString("Y", vec.Y.ToString(Utils.EnUsCulture));
writer.WriteElementString("Z", vec.Z.ToString(Utils.EnUsCulture));
writer.WriteEndElement();
}
static void WriteQuaternion(XmlTextWriter writer, string name, Quaternion quat)
{
writer.WriteStartElement(name);
writer.WriteElementString("X", quat.X.ToString(Utils.EnUsCulture));
writer.WriteElementString("Y", quat.Y.ToString(Utils.EnUsCulture));
writer.WriteElementString("Z", quat.Z.ToString(Utils.EnUsCulture));
writer.WriteElementString("W", quat.W.ToString(Utils.EnUsCulture));
writer.WriteEndElement();
}
#endregion Archive Saving
}
}
@@ -0,0 +1,233 @@
using System;
using System.IO;
using System.Xml;
namespace OpenMetaverse.Assets
{
public class RegionSettings
{
public bool AllowDamage;
public bool AllowLandResell;
public bool AllowLandJoinDivide;
public bool BlockFly;
public bool BlockLandShowInSearch;
public bool BlockTerraform;
public bool DisableCollisions;
public bool DisablePhysics;
public bool DisableScripts;
public int MaturityRating;
public bool RestrictPushing;
public int AgentLimit;
public float ObjectBonus;
public UUID TerrainDetail0;
public UUID TerrainDetail1;
public UUID TerrainDetail2;
public UUID TerrainDetail3;
public float TerrainHeightRange00;
public float TerrainHeightRange01;
public float TerrainHeightRange10;
public float TerrainHeightRange11;
public float TerrainStartHeight00;
public float TerrainStartHeight01;
public float TerrainStartHeight10;
public float TerrainStartHeight11;
public float WaterHeight;
public float TerrainRaiseLimit;
public float TerrainLowerLimit;
public bool UseEstateSun;
public bool FixedSun;
public static RegionSettings FromStream(Stream stream)
{
RegionSettings settings = new RegionSettings();
System.Globalization.NumberFormatInfo nfi = Utils.EnUsCulture.NumberFormat;
using (XmlTextReader xtr = new XmlTextReader(stream))
{
xtr.ReadStartElement("RegionSettings");
xtr.ReadStartElement("General");
while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
{
switch (xtr.Name)
{
case "AllowDamage":
settings.AllowDamage = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "AllowLandResell":
settings.AllowLandResell = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "AllowLandJoinDivide":
settings.AllowLandJoinDivide = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "BlockFly":
settings.BlockFly = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "BlockLandShowInSearch":
settings.BlockLandShowInSearch = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "BlockTerraform":
settings.BlockTerraform = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "DisableCollisions":
settings.DisableCollisions = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "DisablePhysics":
settings.DisablePhysics = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "DisableScripts":
settings.DisableScripts = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "MaturityRating":
settings.MaturityRating = Int32.Parse(xtr.ReadElementContentAsString());
break;
case "RestrictPushing":
settings.RestrictPushing = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "AgentLimit":
settings.AgentLimit = Int32.Parse(xtr.ReadElementContentAsString());
break;
case "ObjectBonus":
settings.ObjectBonus = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
}
}
xtr.ReadEndElement();
xtr.ReadStartElement("GroundTextures");
while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
{
switch (xtr.Name)
{
case "Texture1":
settings.TerrainDetail0 = UUID.Parse(xtr.ReadElementContentAsString());
break;
case "Texture2":
settings.TerrainDetail1 = UUID.Parse(xtr.ReadElementContentAsString());
break;
case "Texture3":
settings.TerrainDetail2 = UUID.Parse(xtr.ReadElementContentAsString());
break;
case "Texture4":
settings.TerrainDetail3 = UUID.Parse(xtr.ReadElementContentAsString());
break;
case "ElevationLowSW":
settings.TerrainStartHeight00 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationLowNW":
settings.TerrainStartHeight01 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationLowSE":
settings.TerrainStartHeight10 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationLowNE":
settings.TerrainStartHeight11 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationHighSW":
settings.TerrainHeightRange00 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationHighNW":
settings.TerrainHeightRange01 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationHighSE":
settings.TerrainHeightRange10 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "ElevationHighNE":
settings.TerrainHeightRange11 = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
}
}
xtr.ReadEndElement();
xtr.ReadStartElement("Terrain");
while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
{
switch (xtr.Name)
{
case "WaterHeight":
settings.WaterHeight = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "TerrainRaiseLimit":
settings.TerrainRaiseLimit = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "TerrainLowerLimit":
settings.TerrainLowerLimit = Single.Parse(xtr.ReadElementContentAsString(), nfi);
break;
case "UseEstateSun":
settings.UseEstateSun = Boolean.Parse(xtr.ReadElementContentAsString());
break;
case "FixedSun":
settings.FixedSun = Boolean.Parse(xtr.ReadElementContentAsString());
break;
}
}
}
return settings;
}
public void ToXML(string filename)
{
StringWriter sw = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(sw) { Formatting = Formatting.Indented };
writer.WriteStartDocument();
writer.WriteStartElement(String.Empty, "RegionSettings", String.Empty);
writer.WriteStartElement(String.Empty, "General", String.Empty);
WriteBoolean(writer, "AllowDamage", AllowDamage);
WriteBoolean(writer, "AllowLandResell", AllowLandResell);
WriteBoolean(writer, "AllowLandJoinDivide", AllowLandJoinDivide);
WriteBoolean(writer, "BlockFly", BlockFly);
WriteBoolean(writer, "BlockLandShowInSearch", BlockLandShowInSearch);
WriteBoolean(writer, "BlockTerraform", BlockTerraform);
WriteBoolean(writer, "DisableCollisions", DisableCollisions);
WriteBoolean(writer, "DisablePhysics", DisablePhysics);
WriteBoolean(writer, "DisableScripts", DisableScripts);
writer.WriteElementString("MaturityRating", MaturityRating.ToString());
WriteBoolean(writer, "RestrictPushing", RestrictPushing);
writer.WriteElementString("AgentLimit", AgentLimit.ToString());
writer.WriteElementString("ObjectBonus", ObjectBonus.ToString());
writer.WriteEndElement();
writer.WriteStartElement(String.Empty, "GroundTextures", String.Empty);
writer.WriteElementString("Texture1", TerrainDetail0.ToString());
writer.WriteElementString("Texture2", TerrainDetail1.ToString());
writer.WriteElementString("Texture3", TerrainDetail2.ToString());
writer.WriteElementString("Texture4", TerrainDetail3.ToString());
writer.WriteElementString("ElevationLowSW", TerrainStartHeight00.ToString());
writer.WriteElementString("ElevationLowNW", TerrainStartHeight01.ToString());
writer.WriteElementString("ElevationLowSE", TerrainStartHeight10.ToString());
writer.WriteElementString("ElevationLowNE", TerrainStartHeight11.ToString());
writer.WriteElementString("ElevationHighSW", TerrainHeightRange00.ToString());
writer.WriteElementString("ElevationHighNW", TerrainHeightRange01.ToString());
writer.WriteElementString("ElevationHighSE", TerrainHeightRange10.ToString());
writer.WriteElementString("ElevationHighNE", TerrainHeightRange11.ToString());
writer.WriteEndElement();
writer.WriteStartElement(String.Empty, "Terrain", String.Empty);
writer.WriteElementString("WaterHeight", WaterHeight.ToString());
writer.WriteElementString("TerrainRaiseLimit", TerrainRaiseLimit.ToString());
writer.WriteElementString("TerrainLowerLimit", TerrainLowerLimit.ToString());
WriteBoolean(writer, "UseEstateSun", UseEstateSun);
WriteBoolean(writer, "FixedSun", FixedSun);
writer.WriteEndElement();
writer.WriteEndElement();
writer.Close();
sw.Close();
File.WriteAllText(filename, sw.ToString());
}
private void WriteBoolean(XmlTextWriter writer, string name, bool value)
{
writer.WriteElementString(name, value ? "True" : "False");
}
}
}
@@ -0,0 +1,224 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Reflection;
using System.Text;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Temporary code to do the bare minimum required to read a tar archive for our purposes
/// </summary>
public class TarArchiveReader
{
public enum TarEntryType
{
TYPE_UNKNOWN = 0,
TYPE_NORMAL_FILE = 1,
TYPE_HARD_LINK = 2,
TYPE_SYMBOLIC_LINK = 3,
TYPE_CHAR_SPECIAL = 4,
TYPE_BLOCK_SPECIAL = 5,
TYPE_DIRECTORY = 6,
TYPE_FIFO = 7,
TYPE_CONTIGUOUS_FILE = 8,
}
protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding();
/// <summary>
/// Binary reader for the underlying stream
/// </summary>
protected BinaryReader m_br;
/// <summary>
/// Used to trim off null chars
/// </summary>
protected static readonly char[] m_nullCharArray = new char[] { '\0' };
/// <summary>
/// Used to trim off space chars
/// </summary>
protected static readonly char[] m_spaceCharArray = new char[] { ' ' };
/// <summary>
/// Generate a tar reader which reads from the given stream.
/// </summary>
/// <param name="s"></param>
public TarArchiveReader(Stream s)
{
m_br = new BinaryReader(s);
}
/// <summary>
/// Read the next entry in the tar file.
/// </summary>
/// <param name="filePath"></param>
/// <param name="entryType"></param>
/// <returns>the data for the entry. Returns null if there are no more entries</returns>
public byte[] ReadEntry(out string filePath, out TarEntryType entryType)
{
filePath = String.Empty;
entryType = TarEntryType.TYPE_UNKNOWN;
TarHeader header = ReadHeader();
if (null == header)
return null;
entryType = header.EntryType;
filePath = header.FilePath;
return ReadData(header.FileSize);
}
/// <summary>
/// Read the next 512 byte chunk of data as a tar header.
/// </summary>
/// <returns>A tar header struct. null if we have reached the end of the archive.</returns>
protected TarHeader ReadHeader()
{
byte[] header = m_br.ReadBytes(512);
// If we've reached the end of the archive we'll be in null block territory, which means
// the next byte will be 0
if (header[0] == 0)
return null;
TarHeader tarHeader = new TarHeader();
// If we're looking at a GNU tar long link then extract the long name and pull up the next header
if (header[156] == (byte)'L')
{
int longNameLength = ConvertOctalBytesToDecimal(header, 124, 11);
tarHeader.FilePath = m_asciiEncoding.GetString(ReadData(longNameLength));
//m_log.DebugFormat("[TAR ARCHIVE READER]: Got long file name {0}", tarHeader.FilePath);
header = m_br.ReadBytes(512);
}
else
{
tarHeader.FilePath = m_asciiEncoding.GetString(header, 0, 100);
tarHeader.FilePath = tarHeader.FilePath.Trim(m_nullCharArray);
//m_log.DebugFormat("[TAR ARCHIVE READER]: Got short file name {0}", tarHeader.FilePath);
}
tarHeader.FileSize = ConvertOctalBytesToDecimal(header, 124, 11);
switch (header[156])
{
case 0:
tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE;
break;
case (byte)'0':
tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE;
break;
case (byte)'1':
tarHeader.EntryType = TarEntryType.TYPE_HARD_LINK;
break;
case (byte)'2':
tarHeader.EntryType = TarEntryType.TYPE_SYMBOLIC_LINK;
break;
case (byte)'3':
tarHeader.EntryType = TarEntryType.TYPE_CHAR_SPECIAL;
break;
case (byte)'4':
tarHeader.EntryType = TarEntryType.TYPE_BLOCK_SPECIAL;
break;
case (byte)'5':
tarHeader.EntryType = TarEntryType.TYPE_DIRECTORY;
break;
case (byte)'6':
tarHeader.EntryType = TarEntryType.TYPE_FIFO;
break;
case (byte)'7':
tarHeader.EntryType = TarEntryType.TYPE_CONTIGUOUS_FILE;
break;
}
return tarHeader;
}
/// <summary>
/// Read data following a header
/// </summary>
/// <param name="fileSize"></param>
/// <returns></returns>
protected byte[] ReadData(int fileSize)
{
byte[] data = m_br.ReadBytes(fileSize);
//m_log.DebugFormat("[TAR ARCHIVE READER]: fileSize {0}", fileSize);
// Read the rest of the empty padding in the 512 byte block
if (fileSize % 512 != 0)
{
int paddingLeft = 512 - (fileSize % 512);
//m_log.DebugFormat("[TAR ARCHIVE READER]: Reading {0} padding bytes", paddingLeft);
m_br.ReadBytes(paddingLeft);
}
return data;
}
public void Close()
{
m_br.Close();
}
/// <summary>
/// Convert octal bytes to a decimal representation
/// </summary>
/// <param name="bytes"></param>
/// <param name="count"></param>
/// <param name="startIndex"></param>
/// <returns></returns>
public static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count)
{
// Trim leading white space: ancient tars do that instead
// of leading 0s :-( don't ask. really.
string oString = m_asciiEncoding.GetString(bytes, startIndex, count).TrimStart(m_spaceCharArray);
int d = 0;
foreach (char c in oString)
{
d <<= 3;
d |= c - '0';
}
return d;
}
}
public class TarHeader
{
public string FilePath;
public int FileSize;
public TarArchiveReader.TarEntryType EntryType;
}
}
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Text;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Temporary code to produce a tar archive in tar v7 format
/// </summary>
public class TarArchiveWriter
{
protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding();
/// <summary>
/// Binary writer for the underlying stream
/// </summary>
protected BinaryWriter m_bw;
public TarArchiveWriter(Stream s)
{
m_bw = new BinaryWriter(s);
}
/// <summary>
/// Write a directory entry to the tar archive. We can only handle one path level right now!
/// </summary>
/// <param name="dirName"></param>
public void WriteDir(string dirName)
{
// Directories are signalled by a final /
if (!dirName.EndsWith("/"))
dirName += "/";
WriteFile(dirName, new byte[0]);
}
/// <summary>
/// Write a file to the tar archive
/// </summary>
/// <param name="filePath"></param>
/// <param name="data"></param>
public void WriteFile(string filePath, string data)
{
WriteFile(filePath, m_asciiEncoding.GetBytes(data));
}
/// <summary>
/// Write a file to the tar archive
/// </summary>
/// <param name="filePath"></param>
/// <param name="data"></param>
public void WriteFile(string filePath, byte[] data)
{
if (filePath.Length > 100)
WriteEntry("././@LongLink", m_asciiEncoding.GetBytes(filePath), 'L');
char fileType;
if (filePath.EndsWith("/"))
{
fileType = '5';
}
else
{
fileType = '0';
}
WriteEntry(filePath, data, fileType);
}
/// <summary>
/// Finish writing the raw tar archive data to a stream. The stream will be closed on completion.
/// </summary>
public void Close()
{
//m_log.Debug("[TAR ARCHIVE WRITER]: Writing final consecutive 0 blocks");
// Write two consecutive 0 blocks to end the archive
byte[] finalZeroPadding = new byte[1024];
m_bw.Write(finalZeroPadding);
m_bw.Flush();
m_bw.Close();
}
public static byte[] ConvertDecimalToPaddedOctalBytes(int d, int padding)
{
string oString = "";
while (d > 0)
{
oString = Convert.ToString((byte)'0' + d & 7) + oString;
d >>= 3;
}
while (oString.Length < padding)
{
oString = "0" + oString;
}
byte[] oBytes = m_asciiEncoding.GetBytes(oString);
return oBytes;
}
/// <summary>
/// Write a particular entry
/// </summary>
/// <param name="filePath"></param>
/// <param name="data"></param>
/// <param name="fileType"></param>
protected void WriteEntry(string filePath, byte[] data, char fileType)
{
byte[] header = new byte[512];
// file path field (100)
byte[] nameBytes = m_asciiEncoding.GetBytes(filePath);
int nameSize = (nameBytes.Length >= 100) ? 100 : nameBytes.Length;
Array.Copy(nameBytes, header, nameSize);
// file mode (8)
byte[] modeBytes = m_asciiEncoding.GetBytes("0000777");
Array.Copy(modeBytes, 0, header, 100, 7);
// owner user id (8)
byte[] ownerIdBytes = m_asciiEncoding.GetBytes("0000764");
Array.Copy(ownerIdBytes, 0, header, 108, 7);
// group user id (8)
byte[] groupIdBytes = m_asciiEncoding.GetBytes("0000764");
Array.Copy(groupIdBytes, 0, header, 116, 7);
// file size in bytes (12)
int fileSize = data.Length;
//m_log.DebugFormat("[TAR ARCHIVE WRITER]: File size of {0} is {1}", filePath, fileSize);
byte[] fileSizeBytes = ConvertDecimalToPaddedOctalBytes(fileSize, 11);
Array.Copy(fileSizeBytes, 0, header, 124, 11);
// last modification time (12)
byte[] lastModTimeBytes = m_asciiEncoding.GetBytes("11017037332");
Array.Copy(lastModTimeBytes, 0, header, 136, 11);
// entry type indicator (1)
header[156] = m_asciiEncoding.GetBytes(new char[] { fileType })[0];
Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 329, 7);
Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 337, 7);
// check sum for header block (8) [calculated last]
Array.Copy(m_asciiEncoding.GetBytes(" "), 0, header, 148, 8);
int checksum = 0;
foreach (byte b in header)
{
checksum += b;
}
//m_log.DebugFormat("[TAR ARCHIVE WRITER]: Decimal header checksum is {0}", checksum);
byte[] checkSumBytes = ConvertDecimalToPaddedOctalBytes(checksum, 6);
Array.Copy(checkSumBytes, 0, header, 148, 6);
header[154] = 0;
// Write out header
m_bw.Write(header);
// Write out data
m_bw.Write(data);
if (data.Length % 512 != 0)
{
int paddingRequired = 512 - (data.Length % 512);
//m_log.DebugFormat("[TAR ARCHIVE WRITER]: Padding data with {0} bytes", paddingRequired);
byte[] padding = new byte[paddingRequired];
m_bw.Write(padding);
}
}
}
}
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Base class for all Asset types
/// </summary>
public abstract class Asset
{
/// <summary>A byte array containing the raw asset data</summary>
public byte[] AssetData;
/// <summary>True if the asset it only stored on the server temporarily</summary>
public bool Temporary;
/// <summary>A unique ID</summary>
private UUID _AssetID;
/// <summary>The assets unique ID</summary>
public UUID AssetID
{
get { return _AssetID; }
internal set { _AssetID = value; }
}
/// <summary>
/// The "type" of asset, Notecard, Animation, etc
/// </summary>
public abstract AssetType AssetType
{
get;
}
/// <summary>
/// Construct a new Asset object
/// </summary>
public Asset() { }
/// <summary>
/// Construct a new Asset object
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public Asset(UUID assetID, byte[] assetData)
{
_AssetID = assetID;
AssetData = assetData;
}
/// <summary>
/// Regenerates the <code>AssetData</code> byte array from the properties
/// of the derived class.
/// </summary>
public abstract void Encode();
/// <summary>
/// Decodes the AssetData, placing it in appropriate properties of the derived
/// class.
/// </summary>
/// <returns>True if the asset decoding succeeded, otherwise false</returns>
public abstract bool Decode();
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents an Animation
/// </summary>
public class AssetAnimation : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Animation; } }
/// <summary>Default Constructor</summary>
public AssetAnimation() { }
/// <summary>
/// Construct an Asset object of type Animation
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetAnimation(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
public override void Encode() { }
public override bool Decode() { return true; }
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents an <seealso cref="AssetWearable"/> that represents an avatars body ie: Hair, Etc.
/// </summary>
public class AssetBodypart : AssetWearable
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Bodypart; } }
/// <summary>Initializes a new instance of an AssetBodyPart object</summary>
public AssetBodypart() { }
/// <summary>Initializes a new instance of an AssetBodyPart object with parameters</summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetBodypart(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents a Callingcard with AvatarID and Position vector
/// </summary>
public class AssetCallingCard : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.CallingCard; } }
/// <summary>UUID of the Callingcard target avatar</summary>
public UUID AvatarID = UUID.Zero;
/// <summary>Construct an Asset of type Callingcard</summary>
public AssetCallingCard() { }
/// <summary>
/// Construct an Asset object of type Callingcard
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetCallingCard(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
Decode();
}
/// <summary>
/// Constuct an asset of type Callingcard
/// </summary>
/// <param name="avatarID">UUID of the target avatar</param>
public AssetCallingCard(UUID avatarID)
{
AvatarID = avatarID;
Encode();
}
/// <summary>
/// Encode the raw contents of a string with the specific Callingcard format
/// </summary>
public override void Encode()
{
string temp = "Callingcard version 2\n";
temp += "avatar_id " + AvatarID + "\n";
AssetData = Utils.StringToBytes(temp);
}
/// <summary>
/// Decode the raw asset data, populating the AvatarID and Position
/// </summary>
/// <returns>true if the AssetData was successfully decoded to a UUID and Vector</returns>
public override bool Decode()
{
String text = Utils.BytesToString(AssetData);
if (text.ToLower().Contains("callingcard version 2"))
{
AvatarID = new UUID(text.Substring(text.IndexOf("avatar_id") + 10, 36));
return true;
}
return false;
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents an <seealso cref="AssetWearable"/> that can be worn on an avatar
/// such as a Shirt, Pants, etc.
/// </summary>
public class AssetClothing : AssetWearable
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Clothing; } }
/// <summary>Initializes a new instance of an AssetScriptBinary object</summary>
public AssetClothing() { }
/// <summary>Initializes a new instance of an AssetScriptBinary object with parameters</summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetClothing(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
}
}
@@ -0,0 +1,465 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using System.Text.RegularExpressions;
using OpenMetaverse;
namespace OpenMetaverse.Assets
{
#region Enums
/// <summary>
/// Type of gesture step
/// </summary>
public enum GestureStepType : int
{
Animation = 0,
Sound,
Chat,
Wait,
EOF
}
#endregion
#region Gesture step classes
/// <summary>
/// Base class for gesture steps
/// </summary>
public abstract class GestureStep
{
/// <summary>
/// Retururns what kind of gesture step this is
/// </summary>
public abstract GestureStepType GestureStepType { get; }
}
/// <summary>
/// Describes animation step of a gesture
/// </summary>
public class GestureStepAnimation : GestureStep
{
/// <summary>
/// Returns what kind of gesture step this is
/// </summary>
public override GestureStepType GestureStepType
{
get { return GestureStepType.Animation; }
}
/// <summary>
/// If true, this step represents start of animation, otherwise animation stop
/// </summary>
public bool AnimationStart = true;
/// <summary>
/// Animation asset <see cref="UUID"/>
/// </summary>
public UUID ID;
/// <summary>
/// Animation inventory name
/// </summary>
public string Name;
public override string ToString()
{
if (AnimationStart)
{
return "Start animation: " + Name;
}
else
{
return "Stop animation: " + Name;
}
}
}
/// <summary>
/// Describes sound step of a gesture
/// </summary>
public class GestureStepSound : GestureStep
{
/// <summary>
/// Returns what kind of gesture step this is
/// </summary>
public override GestureStepType GestureStepType
{
get { return GestureStepType.Sound; }
}
/// <summary>
/// Sound asset <see cref="UUID"/>
/// </summary>
public UUID ID;
/// <summary>
/// Sound inventory name
/// </summary>
public string Name;
public override string ToString()
{
return "Sound: " + Name;
}
}
/// <summary>
/// Describes sound step of a gesture
/// </summary>
public class GestureStepChat : GestureStep
{
/// <summary>
/// Returns what kind of gesture step this is
/// </summary>
public override GestureStepType GestureStepType
{
get { return GestureStepType.Chat; }
}
/// <summary>
/// Text to output in chat
/// </summary>
public string Text;
public override string ToString()
{
return "Chat: " + Text;
}
}
/// <summary>
/// Describes sound step of a gesture
/// </summary>
public class GestureStepWait : GestureStep
{
/// <summary>
/// Returns what kind of gesture step this is
/// </summary>
public override GestureStepType GestureStepType
{
get { return GestureStepType.Wait; }
}
/// <summary>
/// If true in this step we wait for all animations to finish
/// </summary>
public bool WaitForAnimation;
/// <summary>
/// If true gesture player should wait for the specified amount of time
/// </summary>
public bool WaitForTime;
/// <summary>
/// Time in seconds to wait if WaitForAnimation is false
/// </summary>
public float WaitTime;
public override string ToString()
{
StringBuilder ret = new StringBuilder("-- Wait for: ");
if (WaitForAnimation)
{
ret.Append("(animations to finish) ");
}
if (WaitForTime)
{
ret.AppendFormat("(time {0:0.0}s)", WaitTime);
}
return ret.ToString();
}
}
/// <summary>
/// Describes the final step of a gesture
/// </summary>
public class GestureStepEOF : GestureStep
{
/// <summary>
/// Returns what kind of gesture step this is
/// </summary>
public override GestureStepType GestureStepType
{
get { return GestureStepType.EOF; }
}
public override string ToString()
{
return "End of guesture sequence";
}
}
#endregion
/// <summary>
/// Represents a sequence of animations, sounds, and chat actions
/// </summary>
public class AssetGesture : Asset
{
/// <summary>
/// Returns asset type
/// </summary>
public override AssetType AssetType
{
get { return AssetType.Gesture; }
}
/// <summary>
/// Keyboard key that triggers the gestyre
/// </summary>
public byte TriggerKey;
/// <summary>
/// Modifier to the trigger key
/// </summary>
public uint TriggerKeyMask;
/// <summary>
/// String that triggers playing of the gesture sequence
/// </summary>
public string Trigger;
/// <summary>
/// Text that replaces trigger in chat once gesture is triggered
/// </summary>
public string ReplaceWith;
/// <summary>
/// Sequence of gesture steps
/// </summary>
public List<GestureStep> Sequence;
/// <summary>
/// Constructs guesture asset
/// </summary>
public AssetGesture() { }
/// <summary>
/// Constructs guesture asset
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetGesture(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
/// <summary>
/// Encodes gesture asset suitable for uplaod
/// </summary>
public override void Encode()
{
StringBuilder sb = new StringBuilder();
sb.Append("2\n");
sb.Append(TriggerKey + "\n");
sb.Append(TriggerKeyMask + "\n");
sb.Append(Trigger + "\n");
sb.Append(ReplaceWith + "\n");
int count = 0;
if (Sequence != null)
{
count = Sequence.Count;
}
sb.Append(count + "\n");
for (int i = 0; i < count; i++)
{
GestureStep step = Sequence[i];
sb.Append((int)step.GestureStepType + "\n");
switch (step.GestureStepType)
{
case GestureStepType.EOF:
goto Finish;
case GestureStepType.Animation:
GestureStepAnimation animstep = (GestureStepAnimation)step;
sb.Append(animstep.Name + "\n");
sb.Append(animstep.ID + "\n");
if (animstep.AnimationStart)
{
sb.Append("0\n");
}
else
{
sb.Append("1\n");
}
break;
case GestureStepType.Sound:
GestureStepSound soundstep = (GestureStepSound)step;
sb.Append(soundstep.Name + "\n");
sb.Append(soundstep.ID + "\n");
sb.Append("0\n");
break;
case GestureStepType.Chat:
GestureStepChat chatstep = (GestureStepChat)step;
sb.Append(chatstep.Text + "\n");
sb.Append("0\n");
break;
case GestureStepType.Wait:
GestureStepWait waitstep = (GestureStepWait)step;
sb.AppendFormat("{0:0.000000}\n", waitstep.WaitTime);
int waitflags = 0;
if (waitstep.WaitForTime)
{
waitflags |= 0x01;
}
if (waitstep.WaitForAnimation)
{
waitflags |= 0x02;
}
sb.Append(waitflags + "\n");
break;
}
}
Finish:
AssetData = Utils.StringToBytes(sb.ToString());
}
/// <summary>
/// Decodes gesture assset into play sequence
/// </summary>
/// <returns>true if the asset data was decoded successfully</returns>
public override bool Decode()
{
try
{
string[] lines = Utils.BytesToString(AssetData).Split('\n');
Sequence = new List<GestureStep>();
int i = 0;
// version
int version = int.Parse(lines[i++]);
if (version != 2)
{
throw new Exception("Only know how to decode version 2 of gesture asset");
}
TriggerKey = byte.Parse(lines[i++]);
TriggerKeyMask = uint.Parse(lines[i++]);
Trigger = lines[i++];
ReplaceWith = lines[i++];
int count = int.Parse(lines[i++]);
if (count < 0)
{
throw new Exception("Wrong number of gesture steps");
}
for (int n = 0; n < count; n++)
{
GestureStepType type = (GestureStepType)int.Parse(lines[i++]);
switch (type)
{
case GestureStepType.EOF:
goto Finish;
case GestureStepType.Animation:
{
GestureStepAnimation step = new GestureStepAnimation();
step.Name = lines[i++];
step.ID = new UUID(lines[i++]);
int flags = int.Parse(lines[i++]);
if (flags == 0)
{
step.AnimationStart = true;
}
else
{
step.AnimationStart = false;
}
Sequence.Add(step);
break;
}
case GestureStepType.Sound:
{
GestureStepSound step = new GestureStepSound();
step.Name = lines[i++].Replace("\r", "");
step.ID = new UUID(lines[i++]);
int flags = int.Parse(lines[i++]);
Sequence.Add(step);
break;
}
case GestureStepType.Chat:
{
GestureStepChat step = new GestureStepChat();
step.Text = lines[i++];
int flags = int.Parse(lines[i++]);
Sequence.Add(step);
break;
}
case GestureStepType.Wait:
{
GestureStepWait step = new GestureStepWait();
step.WaitTime = float.Parse(lines[i++], Utils.EnUsCulture);
int flags = int.Parse(lines[i++]);
step.WaitForTime = (flags & 0x01) != 0;
step.WaitForAnimation = (flags & 0x02) != 0;
Sequence.Add(step);
break;
}
}
}
Finish:
return true;
}
catch (Exception ex)
{
Logger.Log("Decoding gesture asset failed:" + ex.Message, Helpers.LogLevel.Error);
return false;
}
}
}
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents a Landmark with RegionID and Position vector
/// </summary>
public class AssetLandmark : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Landmark; } }
/// <summary>UUID of the Landmark target region</summary>
public UUID RegionID = UUID.Zero;
/// <summary> Local position of the target </summary>
public Vector3 Position = Vector3.Zero;
/// <summary>Construct an Asset of type Landmark</summary>
public AssetLandmark() { }
/// <summary>
/// Construct an Asset object of type Landmark
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetLandmark(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
/// <summary>
/// Encode the raw contents of a string with the specific Landmark format
/// </summary>
public override void Encode()
{
string temp = "Landmark version 2\n";
temp += "region_id " + RegionID + "\n";
temp += String.Format(Utils.EnUsCulture, "local_pos {0:0.00} {1:0.00} {2:0.00}\n", Position.X, Position.Y, Position.Z);
AssetData = Utils.StringToBytes(temp);
}
/// <summary>
/// Decode the raw asset data, populating the RegionID and Position
/// </summary>
/// <returns>true if the AssetData was successfully decoded to a UUID and Vector</returns>
public override bool Decode()
{
String text = Utils.BytesToString(AssetData);
if (text.ToLower().Contains("landmark version 2"))
{
RegionID = new UUID(text.Substring(text.IndexOf("region_id") + 10, 36));
String vecDelim = " ";
String[] vecStrings = text.Substring(text.IndexOf("local_pos") + 10).Split(vecDelim.ToCharArray());
if (vecStrings.Length == 3)
{
Position = new Vector3(float.Parse(vecStrings[0], System.Globalization.CultureInfo.InvariantCulture), float.Parse(vecStrings[1], System.Globalization.CultureInfo.InvariantCulture), float.Parse(vecStrings[2], System.Globalization.CultureInfo.InvariantCulture));
return true;
}
}
return false;
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents Mesh asset
/// </summary>
public class AssetMesh : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Mesh; } }
/// <summary>
/// Decoded mesh data
/// </summary>
public OSDMap MeshData;
/// <summary>Initializes a new instance of an AssetMesh object</summary>
public AssetMesh() { }
/// <summary>Initializes a new instance of an AssetMesh object with parameters</summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetMesh(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
/// <summary>
/// TODO: Encodes Collada file into LLMesh format
/// </summary>
public override void Encode() { }
/// <summary>
/// Decodes mesh asset. See <see cref="OpenMetaverse.Rendering.FacetedMesh.TryDecodeFromAsset"/>
/// to furter decode it for rendering</summary>
/// <returns>true</returns>
public override bool Decode()
{
try
{
MeshData = new OSDMap();
using (MemoryStream data = new MemoryStream(AssetData))
{
OSDMap header = (OSDMap)OSDParser.DeserializeLLSDBinary(data);
MeshData["asset_header"] = header;
long start = data.Position;
foreach(string partName in header.Keys)
{
if (header[partName].Type != OSDType.Map)
{
MeshData[partName] = header[partName];
continue;
}
OSDMap partInfo = (OSDMap)header[partName];
if (partInfo["offset"] < 0 || partInfo["size"] == 0)
{
MeshData[partName] = partInfo;
continue;
}
byte[] part = new byte[partInfo["size"]];
Buffer.BlockCopy(AssetData, partInfo["offset"] + (int)start, part, 0, part.Length);
MeshData[partName] = Helpers.ZDecompressOSD(part);
}
}
return true;
}
catch (Exception ex)
{
Logger.Log("Failed to decode mesh asset", Helpers.LogLevel.Error, ex);
return false;
}
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents an Animation
/// </summary>
public class AssetMutable : Asset
{
public AssetType currentType;
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return currentType; } }
/// <summary>Default Constructor</summary>
public AssetMutable(AssetType type)
{
currentType = type;
}
/// <summary>
/// Construct an Asset object of type Animation
/// </summary>
/// <param name="type">Asset type</param>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetMutable(AssetType type, UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
currentType = type;
}
public override void Encode() { }
public override bool Decode() { return true; }
}
}
@@ -0,0 +1,401 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using System.Text.RegularExpressions;
using OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents a string of characters encoded with specific formatting properties
/// </summary>
public class AssetNotecard : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Notecard; } }
/// <summary>A text string containing main text of the notecard</summary>
public string BodyText;
/// <summary>List of <see cref="OpenMetaverse.InventoryItem"/>s embedded on the notecard</summary>
public List<InventoryItem> EmbeddedItems;
/// <summary>Construct an Asset of type Notecard</summary>
public AssetNotecard() { }
/// <summary>
/// Construct an Asset object of type Notecard
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetNotecard(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
/// <summary>
/// Encode the raw contents of a string with the specific Linden Text properties
/// </summary>
public override void Encode()
{
string body = BodyText ?? String.Empty;
StringBuilder output = new StringBuilder();
output.Append("Linden text version 2\n");
output.Append("{\n");
output.Append("LLEmbeddedItems version 1\n");
output.Append("{\n");
int count = 0;
if (EmbeddedItems != null)
{
count = EmbeddedItems.Count;
}
output.Append("count " + count + "\n");
if (count > 0)
{
output.Append("{\n");
for (int i = 0; i < EmbeddedItems.Count; i++)
{
InventoryItem item = EmbeddedItems[i];
output.Append("ext char index " + i + "\n");
output.Append("\tinv_item\t0\n");
output.Append("\t{\n");
output.Append("\t\titem_id\t" + item.UUID + "\n");
output.Append("\t\tparent_id\t" + item.ParentUUID + "\n");
output.Append("\tpermissions 0\n");
output.Append("\t{\n");
output.Append("\t\tbase_mask\t" + ((uint)item.Permissions.BaseMask).ToString("x").PadLeft(8, '0') + "\n");
output.Append("\t\towner_mask\t" + ((uint)item.Permissions.OwnerMask).ToString("x").PadLeft(8, '0') + "\n");
output.Append("\t\tgroup_mask\t" + ((uint)item.Permissions.GroupMask).ToString("x").PadLeft(8, '0') + "\n");
output.Append("\t\teveryone_mask\t" + ((uint)item.Permissions.EveryoneMask).ToString("x").PadLeft(8, '0') + "\n");
output.Append("\t\tnext_owner_mask\t" + ((uint)item.Permissions.NextOwnerMask).ToString("x").PadLeft(8, '0') + "\n");
output.Append("\t\tcreator_id\t" + item.CreatorID + "\n");
output.Append("\t\towner_id\t" + item.OwnerID + "\n");
output.Append("\t\tlast_owner_id\t" + item.LastOwnerID + "\n");
output.Append("\t\tgroup_id\t" + item.GroupID + "\n");
if (item.GroupOwned) output.Append("\t\tgroup_owned\t1\n");
output.Append("\t}\n");
if (Permissions.HasPermissions(item.Permissions.BaseMask, PermissionMask.Modify | PermissionMask.Copy | PermissionMask.Transfer) ||
item.AssetUUID == UUID.Zero)
{
output.Append("\t\tasset_id\t" + item.AssetUUID + "\n");
}
else
{
output.Append("\t\tshadow_id\t" + InventoryManager.EncryptAssetID(item.AssetUUID) + "\n");
}
output.Append("\t\ttype\t" + Utils.AssetTypeToString(item.AssetType) + "\n");
output.Append("\t\tinv_type\t" + Utils.InventoryTypeToString(item.InventoryType) + "\n");
output.Append("\t\tflags\t" + item.Flags.ToString().PadLeft(8, '0') + "\n");
output.Append("\tsale_info\t0\n");
output.Append("\t{\n");
output.Append("\t\tsale_type\t" + Utils.SaleTypeToString(item.SaleType) + "\n");
output.Append("\t\tsale_price\t" + item.SalePrice + "\n");
output.Append("\t}\n");
output.Append("\t\tname\t" + item.Name.Replace('|', '_') + "|\n");
output.Append("\t\tdesc\t" + item.Description.Replace('|', '_') + "|\n");
output.Append("\t\tcreation_date\t" + Utils.DateTimeToUnixTime(item.CreationDate) + "\n");
output.Append("\t}\n");
if (i != EmbeddedItems.Count - 1)
{
output.Append("}\n{\n");
}
}
output.Append("}\n");
}
output.Append("}\n");
output.Append("Text length " + (Utils.StringToBytes(body).Length - 1).ToString() + "\n");
output.Append(body + "}\n");
AssetData = Utils.StringToBytes(output.ToString());
}
/// <summary>
/// Decode the raw asset data including the Linden Text properties
/// </summary>
/// <returns>true if the AssetData was successfully decoded</returns>
public override bool Decode()
{
string data = Utils.BytesToString(AssetData);
EmbeddedItems = new List<InventoryItem>();
BodyText = string.Empty;
try
{
string[] lines = data.Split('\n');
int i = 0;
Match m;
// Version
if (!(m = Regex.Match(lines[i++], @"Linden text version\s+(\d+)")).Success)
throw new Exception("could not determine version");
int notecardVersion = int.Parse(m.Groups[1].Value);
if (notecardVersion < 1 || notecardVersion > 2)
throw new Exception("unsuported version");
if (!(m = Regex.Match(lines[i++], @"\s*{$")).Success)
throw new Exception("wrong format");
// Embedded items header
if (!(m = Regex.Match(lines[i++], @"LLEmbeddedItems version\s+(\d+)")).Success)
throw new Exception("could not determine embedded items version version");
if (m.Groups[1].Value != "1")
throw new Exception("unsuported embedded item version");
if (!(m = Regex.Match(lines[i++], @"\s*{$")).Success)
throw new Exception("wrong format");
// Item count
if (!(m = Regex.Match(lines[i++], @"count\s+(\d+)")).Success)
throw new Exception("wrong format");
int count = int.Parse(m.Groups[1].Value);
// Decode individual items
for (int n = 0; n < count; n++)
{
if (!(m = Regex.Match(lines[i++], @"\s*{$")).Success)
throw new Exception("wrong format");
// Index
if (!(m = Regex.Match(lines[i++], @"ext char index\s+(\d+)")).Success)
throw new Exception("missing ext char index");
//warning CS0219: The variable `index' is assigned but its value is never used
//int index = int.Parse(m.Groups[1].Value);
// Inventory item
if (!(m = Regex.Match(lines[i++], @"inv_item\s+0")).Success)
throw new Exception("missing inv item");
// Item itself
UUID uuid = UUID.Zero;
UUID creatorID = UUID.Zero;
UUID ownerID = UUID.Zero;
UUID lastOwnerID = UUID.Zero;
UUID groupID = UUID.Zero;
Permissions permissions = Permissions.NoPermissions;
int salePrice = 0;
SaleType saleType = SaleType.Not;
UUID parentUUID = UUID.Zero;
UUID assetUUID = UUID.Zero;
AssetType assetType = AssetType.Unknown;
InventoryType inventoryType = InventoryType.Unknown;
uint flags = 0;
string name = string.Empty;
string description = string.Empty;
DateTime creationDate = Utils.Epoch;
while (true)
{
if (!(m = Regex.Match(lines[i++], @"([^\s]+)(\s+)?(.*)?")).Success)
throw new Exception("wrong format");
string key = m.Groups[1].Value;
string val = m.Groups[3].Value;
if (key == "{")
continue;
if (key == "}")
break;
else if (key == "permissions")
{
uint baseMask = 0;
uint ownerMask = 0;
uint groupMask = 0;
uint everyoneMask = 0;
uint nextOwnerMask = 0;
while (true)
{
if (!(m = Regex.Match(lines[i++], @"([^\s]+)(\s+)?([^\s]+)?")).Success)
throw new Exception("wrong format");
string pkey = m.Groups[1].Value;
string pval = m.Groups[3].Value;
if (pkey == "{")
continue;
if (pkey == "}")
break;
else if (pkey == "creator_id")
{
creatorID = new UUID(pval);
}
else if (pkey == "owner_id")
{
ownerID = new UUID(pval);
}
else if (pkey == "last_owner_id")
{
lastOwnerID = new UUID(pval);
}
else if (pkey == "group_id")
{
groupID = new UUID(pval);
}
else if (pkey == "base_mask")
{
baseMask = uint.Parse(pval, System.Globalization.NumberStyles.AllowHexSpecifier);
}
else if (pkey == "owner_mask")
{
ownerMask = uint.Parse(pval, System.Globalization.NumberStyles.AllowHexSpecifier);
}
else if (pkey == "group_mask")
{
groupMask = uint.Parse(pval, System.Globalization.NumberStyles.AllowHexSpecifier);
}
else if (pkey == "everyone_mask")
{
everyoneMask = uint.Parse(pval, System.Globalization.NumberStyles.AllowHexSpecifier);
}
else if (pkey == "next_owner_mask")
{
nextOwnerMask = uint.Parse(pval, System.Globalization.NumberStyles.AllowHexSpecifier);
}
}
permissions = new Permissions(baseMask, everyoneMask, groupMask, nextOwnerMask, ownerMask);
}
else if (key == "sale_info")
{
while (true)
{
if (!(m = Regex.Match(lines[i++], @"([^\s]+)(\s+)?([^\s]+)?")).Success)
throw new Exception("wrong format");
string pkey = m.Groups[1].Value;
string pval = m.Groups[3].Value;
if (pkey == "{")
continue;
if (pkey == "}")
break;
else if (pkey == "sale_price")
{
salePrice = int.Parse(pval);
}
else if (pkey == "sale_type")
{
saleType = Utils.StringToSaleType(pval);
}
}
}
else if (key == "item_id")
{
uuid = new UUID(val);
}
else if (key == "parent_id")
{
parentUUID = new UUID(val);
}
else if (key == "asset_id")
{
assetUUID = new UUID(val);
}
else if (key == "type")
{
assetType = Utils.StringToAssetType(val);
}
else if (key == "inv_type")
{
inventoryType = Utils.StringToInventoryType(val);
}
else if (key == "flags")
{
flags = uint.Parse(val, System.Globalization.NumberStyles.AllowHexSpecifier);
}
else if (key == "name")
{
name = val.Remove(val.LastIndexOf("|"));
}
else if (key == "desc")
{
description = val.Remove(val.LastIndexOf("|"));
}
else if (key == "creation_date")
{
creationDate = Utils.UnixTimeToDateTime(int.Parse(val));
}
}
InventoryItem finalEmbedded = InventoryManager.CreateInventoryItem(inventoryType, uuid);
finalEmbedded.CreatorID = creatorID;
finalEmbedded.OwnerID = ownerID;
finalEmbedded.LastOwnerID = lastOwnerID;
finalEmbedded.GroupID = groupID;
finalEmbedded.Permissions = permissions;
finalEmbedded.SalePrice = salePrice;
finalEmbedded.SaleType = saleType;
finalEmbedded.ParentUUID = parentUUID;
finalEmbedded.AssetUUID = assetUUID;
finalEmbedded.AssetType = assetType;
finalEmbedded.Flags = flags;
finalEmbedded.Name = name;
finalEmbedded.Description = description;
finalEmbedded.CreationDate = creationDate;
EmbeddedItems.Add(finalEmbedded);
if (!(m = Regex.Match(lines[i++], @"\s*}$")).Success)
throw new Exception("wrong format");
}
// Text size
if (!(m = Regex.Match(lines[i++], @"\s*}$")).Success)
throw new Exception("wrong format");
if (!(m = Regex.Match(lines[i++], @"Text length\s+(\d+)")).Success)
throw new Exception("could not determine text length");
// Read the rest of the notecard
while (i < lines.Length)
{
BodyText += lines[i++] + "\n";
}
BodyText = BodyText.Remove(BodyText.LastIndexOf("}"));
return true;
}
catch (Exception ex)
{
Logger.Log("Decoding notecard asset failed: " + ex.Message, Helpers.LogLevel.Error);
return false;
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents an AssetScriptBinary object containing the
/// LSO compiled bytecode of an LSL script
/// </summary>
public class AssetScriptBinary : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.LSLBytecode; } }
/// <summary>Initializes a new instance of an AssetScriptBinary object</summary>
public AssetScriptBinary() { }
/// <summary>Initializes a new instance of an AssetScriptBinary object with parameters</summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetScriptBinary(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
/// <summary>
/// TODO: Encodes a scripts contents into a LSO Bytecode file
/// </summary>
public override void Encode() { }
/// <summary>
/// TODO: Decode LSO Bytecode into a string
/// </summary>
/// <returns>true</returns>
public override bool Decode() { return true; }
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents an LSL Text object containing a string of UTF encoded characters
/// </summary>
public class AssetScriptText : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.LSLText; } }
/// <summary>A string of characters represting the script contents</summary>
public string Source;
/// <summary>Initializes a new AssetScriptText object</summary>
public AssetScriptText() { }
/// <summary>
/// Initializes a new AssetScriptText object with parameters
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetScriptText(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
/// <summary>
/// Encode a string containing the scripts contents into byte encoded AssetData
/// </summary>
public override void Encode()
{
AssetData = Utils.StringToBytes(Source);
}
/// <summary>
/// Decode a byte array containing the scripts contents into a string
/// </summary>
/// <returns>true if decoding is successful</returns>
public override bool Decode()
{
Source = Utils.BytesToString(AssetData);
return true;
}
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents a Sound Asset
/// </summary>
public class AssetSound : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Sound; } }
/// <summary>Initializes a new instance of an AssetSound object</summary>
public AssetSound() { }
/// <summary>Initializes a new instance of an AssetSound object with parameters</summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetSound(UUID assetID, byte[] assetData)
: base(assetID, assetData)
{
}
/// <summary>
/// TODO: Encodes a sound file
/// </summary>
public override void Encode() { }
/// <summary>
/// TODO: Decode a sound file
/// </summary>
/// <returns>true</returns>
public override bool Decode() { return true; }
}
}
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse;
using OpenMetaverse.Imaging;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents a texture
/// </summary>
public class AssetTexture : Asset
{
/// <summary>Override the base classes AssetType</summary>
public override AssetType AssetType { get { return AssetType.Texture; } }
/// <summary>A <seealso cref="ManagedImage"/> object containing image data</summary>
public ManagedImage Image;
/// <summary></summary>
public OpenJPEG.J2KLayerInfo[] LayerInfo;
/// <summary></summary>
public int Components;
/// <summary>Initializes a new instance of an AssetTexture object</summary>
public AssetTexture() { }
/// <summary>
/// Initializes a new instance of an AssetTexture object
/// </summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetTexture(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
/// <summary>
/// Initializes a new instance of an AssetTexture object
/// </summary>
/// <param name="image">A <seealso cref="ManagedImage"/> object containing texture data</param>
public AssetTexture(ManagedImage image)
{
Image = image;
Components = 0;
if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0)
Components += 3;
if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0)
++Components;
}
/// <summary>
/// Populates the <seealso cref="AssetData"/> byte array with a JPEG2000
/// encoded image created from the data in <seealso cref="Image"/>
/// </summary>
public override void Encode()
{
AssetData = OpenJPEG.Encode(Image);
}
/// <summary>
/// Decodes the JPEG2000 data in <code>AssetData</code> to the
/// <seealso cref="ManagedImage"/> object <seealso cref="Image"/>
/// </summary>
/// <returns>True if the decoding was successful, otherwise false</returns>
public override bool Decode()
{
if (AssetData != null && AssetData.Length > 0)
{
this.Components = 0;
if (OpenJPEG.DecodeToImage(AssetData, out Image))
{
if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0)
Components += 3;
if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0)
++Components;
if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0)
++Components;
return true;
}
}
return false;
}
/// <summary>
/// Decodes the begin and end byte positions for each quality layer in
/// the image
/// </summary>
/// <returns></returns>
public bool DecodeLayerBoundaries()
{
return OpenJPEG.DecodeLayerBoundaries(AssetData, out LayerInfo, out Components);
}
}
}
@@ -0,0 +1,278 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using OpenMetaverse;
namespace OpenMetaverse.Assets
{
/// <summary>
/// Represents a Wearable Asset, Clothing, Hair, Skin, Etc
/// </summary>
public abstract class AssetWearable : Asset
{
/// <summary>A string containing the name of the asset</summary>
public string Name = String.Empty;
/// <summary>A string containing a short description of the asset</summary>
public string Description = String.Empty;
/// <summary>The Assets WearableType</summary>
public WearableType WearableType = WearableType.Shape;
/// <summary>The For-Sale status of the object</summary>
public SaleType ForSale;
/// <summary>An Integer representing the purchase price of the asset</summary>
public int SalePrice;
/// <summary>The <seealso cref="UUID"/> of the assets creator</summary>
public UUID Creator;
/// <summary>The <seealso cref="UUID"/> of the assets current owner</summary>
public UUID Owner;
/// <summary>The <seealso cref="UUID"/> of the assets prior owner</summary>
public UUID LastOwner;
/// <summary>The <seealso cref="UUID"/> of the Group this asset is set to</summary>
public UUID Group;
/// <summary>True if the asset is owned by a <seealso cref="Group"/></summary>
public bool GroupOwned;
/// <summary>The Permissions mask of the asset</summary>
public Permissions Permissions;
/// <summary>A Dictionary containing Key/Value pairs of the objects parameters</summary>
public Dictionary<int, float> Params = new Dictionary<int, float>();
/// <summary>A Dictionary containing Key/Value pairs where the Key is the textures Index and the Value is the Textures <seealso cref="UUID"/></summary>
public Dictionary<AvatarTextureIndex, UUID> Textures = new Dictionary<AvatarTextureIndex, UUID>();
/// <summary>Initializes a new instance of an AssetWearable object</summary>
public AssetWearable() { }
/// <summary>Initializes a new instance of an AssetWearable object with parameters</summary>
/// <param name="assetID">A unique <see cref="UUID"/> specific to this asset</param>
/// <param name="assetData">A byte array containing the raw asset data</param>
public AssetWearable(UUID assetID, byte[] assetData) : base(assetID, assetData) { }
/// <summary>
/// Decode an assets byte encoded data to a string
/// </summary>
/// <returns>true if the asset data was decoded successfully</returns>
public override bool Decode()
{
if (AssetData == null || AssetData.Length == 0)
return false;
int version = -1;
Permissions = new Permissions();
try
{
string data = Utils.BytesToString(AssetData);
data = data.Replace("\r", String.Empty);
string[] lines = data.Split('\n');
for (int stri = 0; stri < lines.Length; stri++)
{
if (stri == 0)
{
string versionstring = lines[stri];
if (versionstring.Split(' ').Length == 1)
version = Int32.Parse(versionstring);
else
version = Int32.Parse(versionstring.Split(' ')[2]);
if (version != 22 && version != 18 && version != 16 && version != 15)
return false;
}
else if (stri == 1)
{
Name = lines[stri];
}
else if (stri == 2)
{
Description = lines[stri];
}
else
{
string line = lines[stri].Trim();
string[] fields = line.Split('\t');
if (fields.Length == 1)
{
fields = line.Split(' ');
if (fields[0] == "parameters")
{
int count = Int32.Parse(fields[1]) + stri;
for (; stri < count; )
{
stri++;
line = lines[stri].Trim();
fields = line.Split(' ');
int id = 0;
// Special handling for -0 edge case
if (fields[0] != "-0")
id = Int32.Parse(fields[0]);
if (fields[1] == ",")
fields[1] = "0";
else
fields[1] = fields[1].Replace(',', '.');
float weight = float.Parse(fields[1], System.Globalization.NumberStyles.Float,
Utils.EnUsCulture.NumberFormat);
Params[id] = weight;
}
}
else if (fields[0] == "textures")
{
int count = Int32.Parse(fields[1]) + stri;
for (; stri < count; )
{
stri++;
line = lines[stri].Trim();
fields = line.Split(' ');
AvatarTextureIndex id = (AvatarTextureIndex)Int32.Parse(fields[0]);
UUID texture = new UUID(fields[1]);
Textures[id] = texture;
}
}
else if (fields[0] == "type")
{
WearableType = (WearableType)Int32.Parse(fields[1]);
}
}
else if (fields.Length == 2)
{
switch (fields[0])
{
case "creator_mask":
// Deprecated, apply this as the base mask
Permissions.BaseMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "base_mask":
Permissions.BaseMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "owner_mask":
Permissions.OwnerMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "group_mask":
Permissions.GroupMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "everyone_mask":
Permissions.EveryoneMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "next_owner_mask":
Permissions.NextOwnerMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
case "creator_id":
Creator = new UUID(fields[1]);
break;
case "owner_id":
Owner = new UUID(fields[1]);
break;
case "last_owner_id":
LastOwner = new UUID(fields[1]);
break;
case "group_id":
Group = new UUID(fields[1]);
break;
case "group_owned":
GroupOwned = (Int32.Parse(fields[1]) != 0);
break;
case "sale_type":
ForSale = Utils.StringToSaleType(fields[1]);
break;
case "sale_price":
SalePrice = Int32.Parse(fields[1]);
break;
case "sale_info":
// Container for sale_type and sale_price, ignore
break;
case "perm_mask":
// Deprecated, apply this as the next owner mask
Permissions.NextOwnerMask = (PermissionMask)UInt32.Parse(fields[1], System.Globalization.NumberStyles.HexNumber);
break;
default:
return false;
}
}
}
}
}
catch (Exception ex)
{
Logger.Log("Failed decoding wearable asset " + this.AssetID + ": " + ex.Message,
Helpers.LogLevel.Warning);
return false;
}
return true;
}
/// <summary>
/// Encode the assets string represantion into a format consumable by the asset server
/// </summary>
public override void Encode()
{
const string NL = "\n";
StringBuilder data = new StringBuilder("LLWearable version 22\n");
data.Append(Name); data.Append(NL); data.Append(NL);
data.Append("\tpermissions 0\n\t{\n");
data.Append("\t\tbase_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.BaseMask)); data.Append(NL);
data.Append("\t\towner_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.OwnerMask)); data.Append(NL);
data.Append("\t\tgroup_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.GroupMask)); data.Append(NL);
data.Append("\t\teveryone_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.EveryoneMask)); data.Append(NL);
data.Append("\t\tnext_owner_mask\t"); data.Append(Utils.UIntToHexString((uint)Permissions.NextOwnerMask)); data.Append(NL);
data.Append("\t\tcreator_id\t"); data.Append(Creator.ToString()); data.Append(NL);
data.Append("\t\towner_id\t"); data.Append(Owner.ToString()); data.Append(NL);
data.Append("\t\tlast_owner_id\t"); data.Append(LastOwner.ToString()); data.Append(NL);
data.Append("\t\tgroup_id\t"); data.Append(Group.ToString()); data.Append(NL);
if (GroupOwned) data.Append("\t\tgroup_owned\t1\n");
data.Append("\t}\n");
data.Append("\tsale_info\t0\n");
data.Append("\t{\n");
data.Append("\t\tsale_type\t"); data.Append(Utils.SaleTypeToString(ForSale)); data.Append(NL);
data.Append("\t\tsale_price\t"); data.Append(SalePrice); data.Append(NL);
data.Append("\t}\n");
data.Append("type "); data.Append((int)WearableType); data.Append(NL);
data.Append("parameters "); data.Append(Params.Count); data.Append(NL);
foreach (KeyValuePair<int, float> param in Params)
{
data.Append(param.Key); data.Append(" "); data.Append(Helpers.FloatToTerseString(param.Value)); data.Append(NL);
}
data.Append("textures "); data.Append(Textures.Count); data.Append(NL);
foreach (KeyValuePair<AvatarTextureIndex, UUID> texture in Textures)
{
data.Append((byte)texture.Key); data.Append(" "); data.Append(texture.Value.ToString()); data.Append(NL);
}
AssetData = Utils.StringToBytes(data.ToString());
}
}
}
+563
View File
@@ -0,0 +1,563 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using System.Collections.Generic;
using System.Threading;
using OpenMetaverse.Packets;
using OpenMetaverse.StructuredData;
using System.Reflection;
namespace OpenMetaverse
{
#region Enums
/// <summary>
/// Avatar profile flags
/// </summary>
[Flags]
public enum ProfileFlags : uint
{
AllowPublish = 1,
MaturePublish = 2,
Identified = 4,
Transacted = 8,
Online = 16
}
#endregion Enums
/// <summary>
/// Represents an avatar (other than your own)
/// </summary>
public class Avatar : Primitive
{
#region Subclasses
/// <summary>
/// Positive and negative ratings
/// </summary>
public struct Statistics
{
/// <summary>Positive ratings for Behavior</summary>
public int BehaviorPositive;
/// <summary>Negative ratings for Behavior</summary>
public int BehaviorNegative;
/// <summary>Positive ratings for Appearance</summary>
public int AppearancePositive;
/// <summary>Negative ratings for Appearance</summary>
public int AppearanceNegative;
/// <summary>Positive ratings for Building</summary>
public int BuildingPositive;
/// <summary>Negative ratings for Building</summary>
public int BuildingNegative;
/// <summary>Positive ratings given by this avatar</summary>
public int GivenPositive;
/// <summary>Negative ratings given by this avatar</summary>
public int GivenNegative;
public OSD GetOSD()
{
OSDMap tex = new OSDMap(8);
tex["behavior_positive"] = OSD.FromInteger(BehaviorPositive);
tex["behavior_negative"] = OSD.FromInteger(BehaviorNegative);
tex["appearance_positive"] = OSD.FromInteger(AppearancePositive);
tex["appearance_negative"] = OSD.FromInteger(AppearanceNegative);
tex["buildings_positive"] = OSD.FromInteger(BuildingPositive);
tex["buildings_negative"] = OSD.FromInteger(BuildingNegative);
tex["given_positive"] = OSD.FromInteger(GivenPositive);
tex["given_negative"] = OSD.FromInteger(GivenNegative);
return tex;
}
public static Statistics FromOSD(OSD O)
{
Statistics S = new Statistics();
OSDMap tex = (OSDMap)O;
S.BehaviorPositive = tex["behavior_positive"].AsInteger();
S.BuildingNegative = tex["behavior_negative"].AsInteger();
S.AppearancePositive = tex["appearance_positive"].AsInteger();
S.AppearanceNegative = tex["appearance_negative"].AsInteger();
S.BuildingPositive = tex["buildings_positive"].AsInteger();
S.BuildingNegative = tex["buildings_negative"].AsInteger();
S.GivenPositive = tex["given_positive"].AsInteger();
S.GivenNegative = tex["given_negative"].AsInteger();
return S;
}
}
/// <summary>
/// Avatar properties including about text, profile URL, image IDs and
/// publishing settings
/// </summary>
public struct AvatarProperties
{
/// <summary>First Life about text</summary>
public string FirstLifeText;
/// <summary>First Life image ID</summary>
public UUID FirstLifeImage;
/// <summary></summary>
public UUID Partner;
/// <summary></summary>
public string AboutText;
/// <summary></summary>
public string BornOn;
/// <summary></summary>
public string CharterMember;
/// <summary>Profile image ID</summary>
public UUID ProfileImage;
/// <summary>Flags of the profile</summary>
public ProfileFlags Flags;
/// <summary>Web URL for this profile</summary>
public string ProfileURL;
#region Properties
/// <summary>Should this profile be published on the web</summary>
public bool AllowPublish
{
get { return ((Flags & ProfileFlags.AllowPublish) != 0); }
set
{
if (value == true)
Flags |= ProfileFlags.AllowPublish;
else
Flags &= ~ProfileFlags.AllowPublish;
}
}
/// <summary>Avatar Online Status</summary>
public bool Online
{
get { return ((Flags & ProfileFlags.Online) != 0); }
set
{
if (value == true)
Flags |= ProfileFlags.Online;
else
Flags &= ~ProfileFlags.Online;
}
}
/// <summary>Is this a mature profile</summary>
public bool MaturePublish
{
get { return ((Flags & ProfileFlags.MaturePublish) != 0); }
set
{
if (value == true)
Flags |= ProfileFlags.MaturePublish;
else
Flags &= ~ProfileFlags.MaturePublish;
}
}
/// <summary></summary>
public bool Identified
{
get { return ((Flags & ProfileFlags.Identified) != 0); }
set
{
if (value == true)
Flags |= ProfileFlags.Identified;
else
Flags &= ~ProfileFlags.Identified;
}
}
/// <summary></summary>
public bool Transacted
{
get { return ((Flags & ProfileFlags.Transacted) != 0); }
set
{
if (value == true)
Flags |= ProfileFlags.Transacted;
else
Flags &= ~ProfileFlags.Transacted;
}
}
public OSD GetOSD()
{
OSDMap tex = new OSDMap(9);
tex["first_life_text"] = OSD.FromString(FirstLifeText);
tex["first_life_image"] = OSD.FromUUID(FirstLifeImage);
tex["partner"] = OSD.FromUUID(Partner);
tex["about_text"] = OSD.FromString(AboutText);
tex["born_on"] = OSD.FromString(BornOn);
tex["charter_member"] = OSD.FromString(CharterMember);
tex["profile_image"] = OSD.FromUUID(ProfileImage);
tex["flags"] = OSD.FromInteger((byte)Flags);
tex["profile_url"] = OSD.FromString(ProfileURL);
return tex;
}
public static AvatarProperties FromOSD(OSD O)
{
AvatarProperties A = new AvatarProperties();
OSDMap tex = (OSDMap)O;
A.FirstLifeText = tex["first_life_text"].AsString();
A.FirstLifeImage = tex["first_life_image"].AsUUID();
A.Partner = tex["partner"].AsUUID();
A.AboutText = tex["about_text"].AsString();
A.BornOn = tex["born_on"].AsString();
A.CharterMember = tex["chart_member"].AsString();
A.ProfileImage = tex["profile_image"].AsUUID();
A.Flags = (ProfileFlags)tex["flags"].AsInteger();
A.ProfileURL = tex["profile_url"].AsString();
return A;
}
#endregion Properties
}
/// <summary>
/// Avatar interests including spoken languages, skills, and "want to"
/// choices
/// </summary>
public struct Interests
{
/// <summary>Languages profile field</summary>
public string LanguagesText;
/// <summary></summary>
// FIXME:
public uint SkillsMask;
/// <summary></summary>
public string SkillsText;
/// <summary></summary>
// FIXME:
public uint WantToMask;
/// <summary></summary>
public string WantToText;
public OSD GetOSD()
{
OSDMap InterestsOSD = new OSDMap(5);
InterestsOSD["languages_text"] = OSD.FromString(LanguagesText);
InterestsOSD["skills_mask"] = OSD.FromUInteger(SkillsMask);
InterestsOSD["skills_text"] = OSD.FromString(SkillsText);
InterestsOSD["want_to_mask"] = OSD.FromUInteger(WantToMask);
InterestsOSD["want_to_text"] = OSD.FromString(WantToText);
return InterestsOSD;
}
public static Interests FromOSD(OSD O)
{
Interests I = new Interests();
OSDMap tex = (OSDMap)O;
I.LanguagesText = tex["languages_text"].AsString();
I.SkillsMask = tex["skills_mask"].AsUInteger();
I.SkillsText = tex["skills_text"].AsString();
I.WantToMask = tex["want_to_mask"].AsUInteger();
I.WantToText = tex["want_to_text"].AsString();
return I;
}
}
#endregion Subclasses
#region Public Members
/// <summary>Groups that this avatar is a member of</summary>
public List<UUID> Groups = new List<UUID>();
/// <summary>Positive and negative ratings</summary>
public Statistics ProfileStatistics;
/// <summary>Avatar properties including about text, profile URL, image IDs and
/// publishing settings</summary>
public AvatarProperties ProfileProperties;
/// <summary>Avatar interests including spoken languages, skills, and "want to"
/// choices</summary>
public Interests ProfileInterests;
/// <summary>Movement control flags for avatars. Typically not set or used by
/// clients. To move your avatar, use Client.Self.Movement instead</summary>
public AgentManager.ControlFlags ControlFlags;
/// <summary>
/// Contains the visual parameters describing the deformation of the avatar
/// </summary>
public byte[] VisualParameters = null;
/// <summary>
/// Appearance version. Value greater than 0 indicates using server side baking
/// </summary>
public byte AppearanceVersion = 0;
/// <summary>
/// Version of the Current Outfit Folder that the appearance is based on
/// </summary>
public int COFVersion = 0;
/// <summary>
/// Appearance flags. Introduced with server side baking, currently unused.
/// </summary>
public AppearanceFlags AppearanceFlags = AppearanceFlags.None;
/// <summary>
/// List of current avatar animations
/// </summary>
public List<Animation> Animations;
#endregion Public Members
protected string name;
protected string groupName;
#region Properties
/// <summary>First name</summary>
public string FirstName
{
get
{
for (int i = 0; i < NameValues.Length; i++)
{
if (NameValues[i].Name == "FirstName" && NameValues[i].Type == NameValue.ValueType.String)
return (string)NameValues[i].Value;
}
return String.Empty;
}
}
/// <summary>Last name</summary>
public string LastName
{
get
{
for (int i = 0; i < NameValues.Length; i++)
{
if (NameValues[i].Name == "LastName" && NameValues[i].Type == NameValue.ValueType.String)
return (string)NameValues[i].Value;
}
return String.Empty;
}
}
/// <summary>Full name</summary>
public string Name
{
get
{
if (!String.IsNullOrEmpty(name))
{
return name;
}
else if (NameValues != null && NameValues.Length > 0)
{
lock (NameValues)
{
string firstName = String.Empty;
string lastName = String.Empty;
for (int i = 0; i < NameValues.Length; i++)
{
if (NameValues[i].Name == "FirstName" && NameValues[i].Type == NameValue.ValueType.String)
firstName = (string)NameValues[i].Value;
else if (NameValues[i].Name == "LastName" && NameValues[i].Type == NameValue.ValueType.String)
lastName = (string)NameValues[i].Value;
}
if (firstName != String.Empty && lastName != String.Empty)
{
name = String.Format("{0} {1}", firstName, lastName);
return name;
}
else
{
return String.Empty;
}
}
}
else
{
return String.Empty;
}
}
}
/// <summary>Active group</summary>
public string GroupName
{
get
{
if (!String.IsNullOrEmpty(groupName))
{
return groupName;
}
else
{
if (NameValues == null || NameValues.Length == 0)
{
return String.Empty;
}
else
{
lock (NameValues)
{
for (int i = 0; i < NameValues.Length; i++)
{
if (NameValues[i].Name == "Title" && NameValues[i].Type == NameValue.ValueType.String)
{
groupName = (string)NameValues[i].Value;
return groupName;
}
}
}
return String.Empty;
}
}
}
}
public override OSD GetOSD()
{
OSDMap Avi = (OSDMap)base.GetOSD();
OSDArray grp = new OSDArray();
Groups.ForEach(delegate(UUID u) { grp.Add(OSD.FromUUID(u)); });
OSDArray vp = new OSDArray();
for (int i = 0; i < VisualParameters.Length; i++)
{
vp.Add(OSD.FromInteger(VisualParameters[i]));
}
Avi["groups"] = grp;
Avi["profile_statistics"] = ProfileStatistics.GetOSD();
Avi["profile_properties"] = ProfileProperties.GetOSD();
Avi["profile_interest"] = ProfileInterests.GetOSD();
Avi["control_flags"] = OSD.FromInteger((byte)ControlFlags);
Avi["visual_parameters"] = vp;
Avi["first_name"] = OSD.FromString(FirstName);
Avi["last_name"] = OSD.FromString(LastName);
Avi["group_name"] = OSD.FromString(GroupName);
return Avi;
}
public static new Avatar FromOSD(OSD O)
{
OSDMap tex = (OSDMap)O;
Avatar A = new Avatar();
Primitive P = Primitive.FromOSD(O);
Type Prim = typeof(Primitive);
FieldInfo[] Fields = Prim.GetFields();
for (int x = 0; x < Fields.Length; x++)
{
Logger.Log("Field Matched in FromOSD: "+Fields[x].Name, Helpers.LogLevel.Debug);
Fields[x].SetValue(A, Fields[x].GetValue(P));
}
A.Groups = new List<UUID>();
foreach (OSD U in (OSDArray)tex["groups"])
{
A.Groups.Add(U.AsUUID());
}
A.ProfileStatistics = Statistics.FromOSD(tex["profile_statistics"]);
A.ProfileProperties = AvatarProperties.FromOSD(tex["profile_properties"]);
A.ProfileInterests = Interests.FromOSD(tex["profile_interest"]);
A.ControlFlags = (AgentManager.ControlFlags)tex["control_flags"].AsInteger();
OSDArray vp = (OSDArray)tex["visual_parameters"];
A.VisualParameters = new byte[vp.Count];
for (int i = 0; i < vp.Count; i++)
{
A.VisualParameters[i] = (byte)vp[i].AsInteger();
}
// *********************From Code Above *******************************
/*if (NameValues[i].Name == "FirstName" && NameValues[i].Type == NameValue.ValueType.String)
firstName = (string)NameValues[i].Value;
else if (NameValues[i].Name == "LastName" && NameValues[i].Type == NameValue.ValueType.String)
lastName = (string)NameValues[i].Value;*/
// ********************************************************************
A.NameValues = new NameValue[3];
NameValue First = new NameValue();
First.Name = "FirstName";
First.Type = NameValue.ValueType.String;
First.Value = tex["first_name"].AsString();
NameValue Last = new NameValue();
Last.Name = "LastName";
Last.Type = NameValue.ValueType.String;
Last.Value = tex["last_name"].AsString();
// ***************From Code Above***************
// if (NameValues[i].Name == "Title" && NameValues[i].Type == NameValue.ValueType.String)
// *********************************************
NameValue Group = new NameValue();
Group.Name = "Title";
Group.Type = NameValue.ValueType.String;
Group.Value = tex["group_name"].AsString();
A.NameValues[0] = First;
A.NameValues[1] = Last;
A.NameValues[2] = Group;
return A;
}
#endregion Properties
#region Constructors
/// <summary>
/// Default constructor
/// </summary>
public Avatar()
{
}
#endregion Constructors
}
}
File diff suppressed because it is too large Load Diff
+559
View File
@@ -0,0 +1,559 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse
{
/// <summary>
/// Reads in a byte array of an Animation Asset created by the SecondLife(tm) client.
/// </summary>
public class BinBVHAnimationReader
{
/// <summary>
/// Rotation Keyframe count (used internally)
/// </summary>
private int rotationkeys;
/// <summary>
/// Position Keyframe count (used internally)
/// </summary>
private int positionkeys;
public UInt16 unknown0; // Always 1
public UInt16 unknown1; // Always 0
/// <summary>
/// Animation Priority
/// </summary>
public int Priority;
/// <summary>
/// The animation length in seconds.
/// </summary>
public Single Length;
/// <summary>
/// Expression set in the client. Null if [None] is selected
/// </summary>
public string ExpressionName; // "" (null)
/// <summary>
/// The time in seconds to start the animation
/// </summary>
public Single InPoint;
/// <summary>
/// The time in seconds to end the animation
/// </summary>
public Single OutPoint;
/// <summary>
/// Loop the animation
/// </summary>
public bool Loop;
/// <summary>
/// Meta data. Ease in Seconds.
/// </summary>
public Single EaseInTime;
/// <summary>
/// Meta data. Ease out seconds.
/// </summary>
public Single EaseOutTime;
/// <summary>
/// Meta Data for the Hand Pose
/// </summary>
public uint HandPose;
/// <summary>
/// Number of joints defined in the animation
/// </summary>
public uint JointCount;
/// <summary>
/// Contains an array of joints
/// </summary>
public binBVHJoint[] joints;
/// <summary>
/// Searialize an animation asset into it's joints/keyframes/meta data
/// </summary>
/// <param name="animationdata"></param>
public BinBVHAnimationReader(byte[] animationdata)
{
int i = 0;
if (!BitConverter.IsLittleEndian)
{
unknown0 = Utils.BytesToUInt16(EndianSwap(animationdata, i, 2)); i += 2; // Always 1
unknown1 = Utils.BytesToUInt16(EndianSwap(animationdata, i, 2)); i += 2; // Always 0
Priority = Utils.BytesToInt(EndianSwap(animationdata, i, 4)); i += 4;
Length = Utils.BytesToFloat(EndianSwap(animationdata, i, 4), 0); i += 4;
}
else
{
unknown0 = Utils.BytesToUInt16(animationdata, i); i += 2; // Always 1
unknown1 = Utils.BytesToUInt16(animationdata, i); i += 2; // Always 0
Priority = Utils.BytesToInt(animationdata, i); i += 4;
Length = Utils.BytesToFloat(animationdata, i); i += 4;
}
ExpressionName = ReadBytesUntilNull(animationdata, ref i);
if (!BitConverter.IsLittleEndian)
{
InPoint = Utils.BytesToFloat(EndianSwap(animationdata, i, 4), 0); i += 4;
OutPoint = Utils.BytesToFloat(EndianSwap(animationdata, i, 4), 0); i += 4;
Loop = (Utils.BytesToInt(EndianSwap(animationdata, i, 4)) != 0); i += 4;
EaseInTime = Utils.BytesToFloat(EndianSwap(animationdata, i, 4), 0); i += 4;
EaseOutTime = Utils.BytesToFloat(EndianSwap(animationdata, i, 4), 0); i += 4;
HandPose = Utils.BytesToUInt(EndianSwap(animationdata, i, 4)); i += 4; // Handpose?
JointCount = Utils.BytesToUInt(animationdata, i); i += 4; // Get Joint count
}
else
{
InPoint = Utils.BytesToFloat(animationdata, i); i += 4;
OutPoint = Utils.BytesToFloat(animationdata, i); i += 4;
Loop = (Utils.BytesToInt(animationdata, i) != 0); i += 4;
EaseInTime = Utils.BytesToFloat(animationdata, i); i += 4;
EaseOutTime = Utils.BytesToFloat(animationdata, i); i += 4;
HandPose = Utils.BytesToUInt(animationdata, i); i += 4; // Handpose?
JointCount = Utils.BytesToUInt(animationdata, i); i += 4; // Get Joint count
}
joints = new binBVHJoint[JointCount];
// deserialize the number of joints in the animation.
// Joints are variable length blocks of binary data consisting of joint data and keyframes
for (int iter = 0; iter < JointCount; iter++)
{
binBVHJoint joint = readJoint(animationdata, ref i);
joints[iter] = joint;
}
}
private byte[] EndianSwap(byte[] arr, int offset, int len)
{
byte[] bendian = new byte[offset + len];
Buffer.BlockCopy(arr, offset, bendian, 0, len);
Array.Reverse(bendian);
return bendian;
}
/// <summary>
/// Variable length strings seem to be null terminated in the animation asset.. but..
/// use with caution, home grown.
/// advances the index.
/// </summary>
/// <param name="data">The animation asset byte array</param>
/// <param name="i">The offset to start reading</param>
/// <returns>a string</returns>
public string ReadBytesUntilNull(byte[] data, ref int i)
{
char nterm = '\0'; // Null terminator
int endpos = i;
int startpos = i;
// Find the null character
for (int j = i; j < data.Length; j++)
{
char spot = Convert.ToChar(data[j]);
if (spot == nterm)
{
endpos = j;
break;
}
}
// if we got to the end, then it's a zero length string
if (i == endpos)
{
// advance the 1 null character
i++;
return string.Empty;
}
else
{
// We found the end of the string
// append the bytes from the beginning of the string to the end of the string
// advance i
byte[] interm = new byte[endpos - i];
for (; i < endpos; i++)
{
interm[i - startpos] = data[i];
}
i++; // advance past the null character
return Utils.BytesToString(interm);
}
}
/// <summary>
/// Read in a Joint from an animation asset byte array
/// Variable length Joint fields, yay!
/// Advances the index
/// </summary>
/// <param name="data">animation asset byte array</param>
/// <param name="i">Byte Offset of the start of the joint</param>
/// <returns>The Joint data serialized into the binBVHJoint structure</returns>
public binBVHJoint readJoint(byte[] data, ref int i)
{
binBVHJointKey[] positions;
binBVHJointKey[] rotations;
binBVHJoint pJoint = new binBVHJoint();
/*
109
84
111
114
114
111
0 <--- Null terminator
*/
pJoint.Name = ReadBytesUntilNull(data, ref i); // Joint name
/*
2 <- Priority Revisited
0
0
0
*/
/*
5 <-- 5 keyframes
0
0
0
... 5 Keyframe data blocks
*/
/*
2 <-- 2 keyframes
0
0
0
.. 2 Keyframe data blocks
*/
if (!BitConverter.IsLittleEndian)
{
pJoint.Priority = Utils.BytesToInt(EndianSwap(data, i, 4)); i += 4; // Joint Priority override?
rotationkeys = Utils.BytesToInt(EndianSwap(data, i, 4)); i += 4; // How many rotation keyframes
}
else
{
pJoint.Priority = Utils.BytesToInt(data, i); i += 4; // Joint Priority override?
rotationkeys = Utils.BytesToInt(data, i); i += 4; // How many rotation keyframes
}
// Sanity check how many rotation keys there are
if (rotationkeys < 0 || rotationkeys > 10000)
{
rotationkeys = 0;
}
rotations = readKeys(data, ref i, rotationkeys, -1.0f, 1.0f);
if (!BitConverter.IsLittleEndian)
{
positionkeys = Utils.BytesToInt(EndianSwap(data, i, 4)); i += 4; // How many position keyframes
}
else
{
positionkeys = Utils.BytesToInt(data, i); i += 4; // How many position keyframes
}
// Sanity check how many positions keys there are
if (positionkeys < 0 || positionkeys > 10000)
{
positionkeys = 0;
}
// Read in position keyframes
positions = readKeys(data, ref i, positionkeys, -0.5f, 1.5f);
pJoint.rotationkeys = rotations;
pJoint.positionkeys = positions;
return pJoint;
}
/// <summary>
/// Read Keyframes of a certain type
/// advance i
/// </summary>
/// <param name="data">Animation Byte array</param>
/// <param name="i">Offset in the Byte Array. Will be advanced</param>
/// <param name="keycount">Number of Keyframes</param>
/// <param name="min">Scaling Min to pass to the Uint16ToFloat method</param>
/// <param name="max">Scaling Max to pass to the Uint16ToFloat method</param>
/// <returns></returns>
public binBVHJointKey[] readKeys(byte[] data, ref int i, int keycount, float min, float max)
{
float x;
float y;
float z;
/*
17 255 <-- Time Code
17 255 <-- Time Code
255 255 <-- X
127 127 <-- X
255 255 <-- Y
127 127 <-- Y
213 213 <-- Z
142 142 <---Z
*/
binBVHJointKey[] m_keys = new binBVHJointKey[keycount];
for (int j = 0; j < keycount; j++)
{
binBVHJointKey pJKey = new binBVHJointKey();
if (!BitConverter.IsLittleEndian)
{
pJKey.time = Utils.UInt16ToFloat(EndianSwap(data, i, 2), 0, InPoint, OutPoint); i += 2;
x = Utils.UInt16ToFloat(EndianSwap(data, i, 2), 0, min, max); i += 2;
y = Utils.UInt16ToFloat(EndianSwap(data, i, 2), 0, min, max); i += 2;
z = Utils.UInt16ToFloat(EndianSwap(data, i, 2), 0, min, max); i += 2;
}
else
{
pJKey.time = Utils.UInt16ToFloat(data, i, InPoint, OutPoint); i += 2;
x = Utils.UInt16ToFloat(data, i, min, max); i += 2;
y = Utils.UInt16ToFloat(data, i, min, max); i += 2;
z = Utils.UInt16ToFloat(data, i, min, max); i += 2;
}
pJKey.key_element = new Vector3(x, y, z);
m_keys[j] = pJKey;
}
return m_keys;
}
public bool Equals(BinBVHAnimationReader other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Loop.Equals(Loop) && other.OutPoint == OutPoint && other.InPoint == InPoint && other.Length == Length && other.HandPose == HandPose && other.JointCount == JointCount && Equals(other.joints, joints) && other.EaseInTime == EaseInTime && other.EaseOutTime == EaseOutTime && other.Priority == Priority && other.unknown1 == unknown1 && other.unknown0 == unknown0 && other.positionkeys == positionkeys && other.rotationkeys == rotationkeys;
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.
/// </param><exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.
/// </exception><filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(BinBVHAnimationReader)) return false;
return Equals((BinBVHAnimationReader)obj);
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterpriority>2</filterpriority>
public override int GetHashCode()
{
unchecked
{
int result = Loop.GetHashCode();
result = (result * 397) ^ OutPoint.GetHashCode();
result = (result * 397) ^ InPoint.GetHashCode();
result = (result * 397) ^ Length.GetHashCode();
result = (result * 397) ^ HandPose.GetHashCode();
result = (result * 397) ^ JointCount.GetHashCode();
result = (result * 397) ^ (joints != null ? joints.GetHashCode() : 0);
result = (result * 397) ^ EaseInTime.GetHashCode();
result = (result * 397) ^ EaseOutTime.GetHashCode();
result = (result * 397) ^ Priority;
result = (result * 397) ^ unknown1.GetHashCode();
result = (result * 397) ^ unknown0.GetHashCode();
result = (result * 397) ^ positionkeys;
result = (result * 397) ^ rotationkeys;
return result;
}
}
public static bool Equals(binBVHJoint[] arr1, binBVHJoint[] arr2)
{
if (arr1.Length == arr2.Length)
{
for (int i = 0; i < arr1.Length; i++)
if (!arr1[i].Equals(arr2[i]))
return false;
/* not same*/
return true;
}
return false;
}
}
/// <summary>
/// A Joint and it's associated meta data and keyframes
/// </summary>
public struct binBVHJoint
{
public static bool Equals(binBVHJointKey[] arr1, binBVHJointKey[] arr2)
{
if (arr1.Length == arr2.Length)
{
for (int i = 0; i < arr1.Length; i++)
if (!Equals(arr1[i], arr2[i]))
return false;
/* not same*/
return true;
}
return false;
}
public static bool Equals(binBVHJointKey arr1, binBVHJointKey arr2)
{
return (arr1.time == arr2.time && arr1.key_element == arr2.key_element);
}
public bool Equals(binBVHJoint other)
{
return other.Priority == Priority && Equals(other.rotationkeys, rotationkeys) && Equals(other.Name, Name) && Equals(other.positionkeys, positionkeys);
}
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
/// <returns>
/// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
/// </returns>
/// <param name="obj">Another object to compare to.
/// </param><filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof(binBVHJoint)) return false;
return Equals((binBVHJoint)obj);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
/// <filterpriority>2</filterpriority>
public override int GetHashCode()
{
unchecked
{
int result = Priority;
result = (result * 397) ^ (rotationkeys != null ? rotationkeys.GetHashCode() : 0);
result = (result * 397) ^ (Name != null ? Name.GetHashCode() : 0);
result = (result * 397) ^ (positionkeys != null ? positionkeys.GetHashCode() : 0);
return result;
}
}
public static bool operator ==(binBVHJoint left, binBVHJoint right)
{
return left.Equals(right);
}
public static bool operator !=(binBVHJoint left, binBVHJoint right)
{
return !left.Equals(right);
}
/// <summary>
/// Name of the Joint. Matches the avatar_skeleton.xml in client distros
/// </summary>
public string Name;
/// <summary>
/// Joint Animation Override? Was the same as the Priority in testing..
/// </summary>
public int Priority;
/// <summary>
/// Array of Rotation Keyframes in order from earliest to latest
/// </summary>
public binBVHJointKey[] rotationkeys;
/// <summary>
/// Array of Position Keyframes in order from earliest to latest
/// This seems to only be for the Pelvis?
/// </summary>
public binBVHJointKey[] positionkeys;
/// <summary>
/// Custom application data that can be attached to a joint
/// </summary>
public object Tag;
}
/// <summary>
/// A Joint Keyframe. This is either a position or a rotation.
/// </summary>
public struct binBVHJointKey
{
// Time in seconds for this keyframe.
public float time;
/// <summary>
/// Either a Vector3 position or a Vector3 Euler rotation
/// </summary>
public Vector3 key_element;
}
/// <summary>
/// Poses set in the animation metadata for the hands.
/// </summary>
public enum HandPose : uint
{
Spread = 0,
Relaxed = 1,
Point_Both = 2,
Fist = 3,
Relaxed_Left = 4,
Point_Left = 5,
Fist_Left = 6,
Relaxed_Right = 7,
Point_Right = 8,
Fist_Right = 9,
Salute_Right = 10,
Typing = 11,
Peace_Right = 12
}
}
+417
View File
@@ -0,0 +1,417 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse
{
/// <summary>
/// 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
/// </summary>
public class BitPack
{
/// <summary></summary>
public byte[] Data;
/// <summary></summary>
public int BytePos
{
get
{
if (bytePos != 0 && bitPos == 0)
return bytePos - 1;
else
return bytePos;
}
}
/// <summary></summary>
public int BitPos { get { return bitPos; } }
private const int MAX_BITS = 8;
private static readonly byte[] ON = new byte[] { 1 };
private static readonly byte[] OFF = new byte[] { 0 };
private int bytePos;
private int bitPos;
private bool weAreBigEndian = !BitConverter.IsLittleEndian;
/// <summary>
/// Default constructor, initialize the bit packer / bit unpacker
/// with a byte array and starting position
/// </summary>
/// <param name="data">Byte array to pack bits in to or unpack from</param>
/// <param name="pos">Starting position in the byte array</param>
public BitPack(byte[] data, int pos)
{
Data = data;
bytePos = pos;
}
/// <summary>
/// Pack a floating point value in to the data
/// </summary>
/// <param name="data">Floating point value to pack</param>
public void PackFloat(float data)
{
byte[] input = BitConverter.GetBytes(data);
if (weAreBigEndian) Array.Reverse(input);
PackBitArray(input, 32);
}
/// <summary>
/// Pack part or all of an integer in to the data
/// </summary>
/// <param name="data">Integer containing the data to pack</param>
/// <param name="totalCount">Number of bits of the integer to pack</param>
public void PackBits(int data, int totalCount)
{
byte[] input = BitConverter.GetBytes(data);
if (weAreBigEndian) Array.Reverse(input);
PackBitArray(input, totalCount);
}
/// <summary>
/// Pack part or all of an unsigned integer in to the data
/// </summary>
/// <param name="data">Unsigned integer containing the data to pack</param>
/// <param name="totalCount">Number of bits of the integer to pack</param>
public void PackBits(uint data, int totalCount)
{
byte[] input = BitConverter.GetBytes(data);
if (weAreBigEndian) Array.Reverse(input);
PackBitArray(input, totalCount);
}
/// <summary>
/// Pack a single bit in to the data
/// </summary>
/// <param name="bit">Bit to pack</param>
public void PackBit(bool bit)
{
if (bit)
PackBitArray(ON, 1);
else
PackBitArray(OFF, 1);
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="isSigned"></param>
/// <param name="intBits"></param>
/// <param name="fracBits"></param>
public void PackFixed(float data, bool isSigned, int intBits, int fracBits)
{
int unsignedBits = intBits + fracBits;
int totalBits = unsignedBits;
int min, max;
if (isSigned)
{
totalBits++;
min = 1 << intBits;
min *= -1;
}
else
{
min = 0;
}
max = 1 << intBits;
float fixedVal = Utils.Clamp(data, (float)min, (float)max);
if (isSigned) fixedVal += max;
fixedVal *= 1 << fracBits;
if (totalBits <= 8)
PackBits((uint)fixedVal, 8);
else if (totalBits <= 16)
PackBits((uint)fixedVal, 16);
else if (totalBits <= 31)
PackBits((uint)fixedVal, 32);
else
throw new Exception("Can't use fixed point packing for " + totalBits);
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
public void PackUUID(UUID data)
{
byte[] bytes = data.GetBytes();
// Not sure if our PackBitArray function can handle 128-bit byte
//arrays, so using this for now
for (int i = 0; i < 16; i++)
PackBits(bytes[i], 8);
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
public void PackColor(Color4 data)
{
byte[] bytes = data.GetBytes();
PackBitArray(bytes, 32);
}
/// <summary>
/// Unpacking a floating point value from the data
/// </summary>
/// <returns>Unpacked floating point value</returns>
public float UnpackFloat()
{
byte[] output = UnpackBitsArray(32);
if (weAreBigEndian) Array.Reverse(output);
return BitConverter.ToSingle(output, 0);
}
/// <summary>
/// Unpack a variable number of bits from the data in to integer format
/// </summary>
/// <param name="totalCount">Number of bits to unpack</param>
/// <returns>An integer containing the unpacked bits</returns>
/// <remarks>This function is only useful up to 32 bits</remarks>
public int UnpackBits(int totalCount)
{
byte[] output = UnpackBitsArray(totalCount);
if (weAreBigEndian) Array.Reverse(output);
return BitConverter.ToInt32(output, 0);
}
/// <summary>
/// Unpack a variable number of bits from the data in to unsigned
/// integer format
/// </summary>
/// <param name="totalCount">Number of bits to unpack</param>
/// <returns>An unsigned integer containing the unpacked bits</returns>
/// <remarks>This function is only useful up to 32 bits</remarks>
public uint UnpackUBits(int totalCount)
{
byte[] output = UnpackBitsArray(totalCount);
if (weAreBigEndian) Array.Reverse(output);
return BitConverter.ToUInt32(output, 0);
}
/// <summary>
/// Unpack a 16-bit signed integer
/// </summary>
/// <returns>16-bit signed integer</returns>
public short UnpackShort()
{
return (short)UnpackBits(16);
}
/// <summary>
/// Unpack a 16-bit unsigned integer
/// </summary>
/// <returns>16-bit unsigned integer</returns>
public ushort UnpackUShort()
{
return (ushort)UnpackUBits(16);
}
/// <summary>
/// Unpack a 32-bit signed integer
/// </summary>
/// <returns>32-bit signed integer</returns>
public int UnpackInt()
{
return UnpackBits(32);
}
/// <summary>
/// Unpack a 32-bit unsigned integer
/// </summary>
/// <returns>32-bit unsigned integer</returns>
public uint UnpackUInt()
{
return UnpackUBits(32);
}
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 string UnpackString(int size)
{
if (bitPos != 0 || bytePos + size > Data.Length) throw new IndexOutOfRangeException();
string str = System.Text.UTF8Encoding.UTF8.GetString(Data, bytePos, size);
bytePos += size;
return str;
}
public UUID UnpackUUID()
{
if (bitPos != 0) throw new IndexOutOfRangeException();
UUID val = new UUID(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)
{
byte curBit = (byte)(0x80 >> bitPos);
if ((data[curBytePos] & (0x01 << (count - 1))) != 0)
Data[bytePos] |= curBit;
else
Data[bytePos] &= (byte)~curBit;
--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;
}
}
}
+280
View File
@@ -0,0 +1,280 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using System.Net.Security;
using System.IO;
using System.Text;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
namespace OpenMetaverse.Http
{
public class TrustAllCertificatePolicy : ICertificatePolicy
{
public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest req, int problem)
{
return true;
}
public static bool TrustAllCertificateHandler(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
}
public static class CapsBase
{
public delegate void OpenWriteEventHandler(HttpWebRequest request);
public delegate void DownloadProgressEventHandler(HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive);
public delegate void RequestCompletedEventHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error);
static CapsBase()
{
ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
// Even though this will compile on Mono 2.4, it throws a runtime exception
//ServicePointManager.ServerCertificateValidationCallback = TrustAllCertificatePolicy.TrustAllCertificateHandler;
}
private class RequestState
{
public HttpWebRequest Request;
public byte[] UploadData;
public int MillisecondsTimeout;
public OpenWriteEventHandler OpenWriteCallback;
public DownloadProgressEventHandler DownloadProgressCallback;
public RequestCompletedEventHandler CompletedCallback;
public RequestState(HttpWebRequest request, byte[] uploadData, int millisecondsTimeout, OpenWriteEventHandler openWriteCallback,
DownloadProgressEventHandler downloadProgressCallback, RequestCompletedEventHandler completedCallback)
{
Request = request;
UploadData = uploadData;
MillisecondsTimeout = millisecondsTimeout;
OpenWriteCallback = openWriteCallback;
DownloadProgressCallback = downloadProgressCallback;
CompletedCallback = completedCallback;
}
}
public static HttpWebRequest UploadDataAsync(Uri address, X509Certificate2 clientCert, string contentType, byte[] data,
int millisecondsTimeout, OpenWriteEventHandler openWriteCallback, DownloadProgressEventHandler downloadProgressCallback,
RequestCompletedEventHandler completedCallback)
{
// Create the request
HttpWebRequest request = SetupRequest(address, clientCert);
request.ContentLength = data.Length;
if (!String.IsNullOrEmpty(contentType))
request.ContentType = contentType;
request.Method = "POST";
// Create an object to hold all of the state for this request
RequestState state = new RequestState(request, data, millisecondsTimeout, openWriteCallback,
downloadProgressCallback, completedCallback);
// Start the request for a stream to upload to
IAsyncResult result = request.BeginGetRequestStream(OpenWrite, state);
// Register a timeout for the request
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, state, millisecondsTimeout, true);
return request;
}
public static HttpWebRequest DownloadStringAsync(Uri address, X509Certificate2 clientCert, int millisecondsTimeout,
DownloadProgressEventHandler downloadProgressCallback, RequestCompletedEventHandler completedCallback)
{
// Create the request
HttpWebRequest request = SetupRequest(address, clientCert);
request.Method = "GET";
DownloadDataAsync(request, millisecondsTimeout, downloadProgressCallback, completedCallback);
return request;
}
public static void DownloadDataAsync(HttpWebRequest request, int millisecondsTimeout,
DownloadProgressEventHandler downloadProgressCallback, RequestCompletedEventHandler completedCallback)
{
// Create an object to hold all of the state for this request
RequestState state = new RequestState(request, null, millisecondsTimeout, null, downloadProgressCallback,
completedCallback);
// Start the request for the remote server response
IAsyncResult result = request.BeginGetResponse(GetResponse, state);
// Register a timeout for the request
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, state, millisecondsTimeout, true);
}
static HttpWebRequest SetupRequest(Uri address, X509Certificate2 clientCert)
{
if (address == null)
throw new ArgumentNullException("address");
// Create the request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(address);
// Add the client certificate to the request if one was given
if (clientCert != null)
request.ClientCertificates.Add(clientCert);
// Leave idle connections to this endpoint open for up to 60 seconds
request.ServicePoint.MaxIdleTime = 1000 * 60;
// Disable stupid Expect-100: Continue header
request.ServicePoint.Expect100Continue = false;
// Crank up the max number of connections per endpoint
// We set this manually here instead of in ServicePointManager to avoid intereference with callers.
if (request.ServicePoint.ConnectionLimit < Settings.MAX_HTTP_CONNECTIONS)
{
Logger.Log(
string.Format(
"In CapsBase.SetupRequest() setting conn limit for {0}:{1} to {2}",
address.Host, address.Port, Settings.MAX_HTTP_CONNECTIONS), Helpers.LogLevel.Debug);
request.ServicePoint.ConnectionLimit = Settings.MAX_HTTP_CONNECTIONS;
}
// Caps requests are never sent as trickles of data, so Nagle's
// coalescing algorithm won't help us
request.ServicePoint.UseNagleAlgorithm = false;
// If not on mono, set accept-encoding header that allows response compression
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
return request;
}
static void OpenWrite(IAsyncResult ar)
{
RequestState state = (RequestState)ar.AsyncState;
try
{
// Get the stream to write our upload to
using (Stream uploadStream = state.Request.EndGetRequestStream(ar))
{
// Fire the callback for successfully opening the stream
if (state.OpenWriteCallback != null)
state.OpenWriteCallback(state.Request);
// Write our data to the upload stream
uploadStream.Write(state.UploadData, 0, state.UploadData.Length);
}
// Start the request for the remote server response
IAsyncResult result = state.Request.BeginGetResponse(GetResponse, state);
// Register a timeout for the request
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, state,
state.MillisecondsTimeout, true);
}
catch (Exception ex)
{
//Logger.Log.Debug("CapsBase.OpenWrite(): " + ex.Message);
if (state.CompletedCallback != null)
state.CompletedCallback(state.Request, null, null, ex);
}
}
static void GetResponse(IAsyncResult ar)
{
RequestState state = (RequestState)ar.AsyncState;
HttpWebResponse response = null;
byte[] responseData = null;
Exception error = null;
try
{
using (response = (HttpWebResponse)state.Request.EndGetResponse(ar))
{
// Get the stream for downloading the response
using (Stream responseStream = response.GetResponseStream())
{
#region Read the response
// If Content-Length is set we create a buffer of the exact size, otherwise
// a MemoryStream is used to receive the response
bool nolength = (response.ContentLength <= 0) || (Type.GetType("Mono.Runtime") != null);
int size = (nolength) ? 8192 : (int)response.ContentLength;
MemoryStream ms = (nolength) ? new MemoryStream() : null;
byte[] buffer = new byte[size];
int bytesRead = 0;
int offset = 0;
int totalBytesRead = 0;
int totalSize = nolength ? 0 : size;
while ((bytesRead = responseStream.Read(buffer, offset, size)) != 0)
{
totalBytesRead += bytesRead;
if (nolength)
{
totalSize += (size - bytesRead);
ms.Write(buffer, 0, bytesRead);
}
else
{
offset += bytesRead;
size -= bytesRead;
}
// Fire the download progress callback for each chunk of received data
if (state.DownloadProgressCallback != null)
state.DownloadProgressCallback(state.Request, response, totalBytesRead, totalSize);
}
if (nolength)
{
responseData = ms.ToArray();
ms.Close();
ms.Dispose();
}
else
{
responseData = buffer;
}
#endregion Read the response
}
}
}
catch (Exception ex)
{
// Logger.DebugLog("CapsBase.GetResponse(): " + ex.Message);
error = ex;
}
if (state.CompletedCallback != null)
state.CompletedCallback(state.Request, response, responseData, error);
}
static void TimeoutCallback(object state, bool timedOut)
{
if (timedOut)
{
RequestState requestState = state as RequestState;
//Logger.Log.Debug("CapsBase.TimeoutCallback(): Request to " + requestState.Request.RequestUri +
// " timed out after " + requestState.MillisecondsTimeout + " milliseconds");
if (requestState != null && requestState.Request != null)
requestState.Request.Abort();
}
}
}
}
+187
View File
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.Http
{
public class CapsClient
{
public delegate void DownloadProgressCallback(CapsClient client, int bytesReceived, int totalBytesToReceive);
public delegate void CompleteCallback(CapsClient client, OSD result, Exception error);
public event DownloadProgressCallback OnDownloadProgress;
public event CompleteCallback OnComplete;
public object UserData;
protected Uri _Address;
protected byte[] _PostData;
protected X509Certificate2 _ClientCert;
protected string _ContentType;
protected HttpWebRequest _Request;
protected OSD _Response;
protected AutoResetEvent _ResponseEvent = new AutoResetEvent(false);
public CapsClient(Uri capability)
: this(capability, null)
{
}
public CapsClient(Uri capability, X509Certificate2 clientCert)
{
_Address = capability;
_ClientCert = clientCert;
}
public void BeginGetResponse(int millisecondsTimeout)
{
BeginGetResponse(null, null, millisecondsTimeout);
}
public void BeginGetResponse(OSD data, OSDFormat format, int millisecondsTimeout)
{
byte[] postData;
string contentType;
switch (format)
{
case OSDFormat.Xml:
postData = OSDParser.SerializeLLSDXmlBytes(data);
contentType = "application/llsd+xml";
break;
case OSDFormat.Binary:
postData = OSDParser.SerializeLLSDBinary(data);
contentType = "application/llsd+binary";
break;
case OSDFormat.Json:
default:
postData = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data));
contentType = "application/llsd+json";
break;
}
BeginGetResponse(postData, contentType, millisecondsTimeout);
}
public void BeginGetResponse(byte[] postData, string contentType, int millisecondsTimeout)
{
_PostData = postData;
_ContentType = contentType;
if (_Request != null)
{
_Request.Abort();
_Request = null;
}
if (postData == null)
{
// GET
//Logger.Log.Debug("[CapsClient] GET " + _Address);
_Request = CapsBase.DownloadStringAsync(_Address, _ClientCert, millisecondsTimeout, DownloadProgressHandler,
RequestCompletedHandler);
}
else
{
// POST
//Logger.Log.Debug("[CapsClient] POST (" + postData.Length + " bytes) " + _Address);
_Request = CapsBase.UploadDataAsync(_Address, _ClientCert, contentType, postData, millisecondsTimeout, null,
DownloadProgressHandler, RequestCompletedHandler);
}
}
public OSD GetResponse(int millisecondsTimeout)
{
BeginGetResponse(millisecondsTimeout);
_ResponseEvent.WaitOne(millisecondsTimeout, false);
return _Response;
}
public OSD GetResponse(OSD data, OSDFormat format, int millisecondsTimeout)
{
BeginGetResponse(data, format, millisecondsTimeout);
_ResponseEvent.WaitOne(millisecondsTimeout, false);
return _Response;
}
public OSD GetResponse(byte[] postData, string contentType, int millisecondsTimeout)
{
BeginGetResponse(postData, contentType, millisecondsTimeout);
_ResponseEvent.WaitOne(millisecondsTimeout, false);
return _Response;
}
public void Cancel()
{
if (_Request != null)
_Request.Abort();
}
void DownloadProgressHandler(HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive)
{
_Request = request;
if (OnDownloadProgress != null)
{
try { OnDownloadProgress(this, bytesReceived, totalBytesToReceive); }
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
}
}
void RequestCompletedHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error)
{
_Request = request;
OSD result = null;
if (responseData != null)
{
try { result = OSDParser.Deserialize(responseData); }
catch (Exception ex) { error = ex; }
}
FireCompleteCallback(result, error);
}
private void FireCompleteCallback(OSD result, Exception error)
{
CompleteCallback callback = OnComplete;
if (callback != null)
{
try { callback(this, result, error); }
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
}
_Response = result;
_ResponseEvent.Set();
}
}
}
@@ -0,0 +1,242 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using System.Threading;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.Http
{
public class EventQueueClient
{
/// <summary>=</summary>
public const int REQUEST_TIMEOUT = 1000 * 120;
public delegate void ConnectedCallback();
public delegate void EventCallback(string eventName, OSDMap body);
public ConnectedCallback OnConnected;
public EventCallback OnEvent;
public bool Running { get { return _Running; } }
protected Uri _Address;
protected bool _Dead;
protected bool _Running;
protected HttpWebRequest _Request;
/// <summary>Number of times we've received an unknown CAPS exception in series.</summary>
private int _errorCount;
/// <summary>For exponential backoff on error.</summary>
private static Random _random = new Random();
public EventQueueClient(Uri eventQueueLocation)
{
_Address = eventQueueLocation;
}
public void Start()
{
_Dead = false;
// Create an EventQueueGet request
OSDMap request = new OSDMap();
request["ack"] = new OSD();
request["done"] = OSD.FromBoolean(false);
byte[] postData = OSDParser.SerializeLLSDXmlBytes(request);
_Request = CapsBase.UploadDataAsync(_Address, null, "application/xml", postData, REQUEST_TIMEOUT, OpenWriteHandler, null, RequestCompletedHandler);
}
public void Stop(bool immediate)
{
_Dead = true;
if (immediate)
_Running = false;
if (_Request != null)
_Request.Abort();
}
void OpenWriteHandler(HttpWebRequest request)
{
_Running = true;
_Request = request;
Logger.DebugLog("Capabilities event queue connected");
// The event queue is starting up for the first time
if (OnConnected != null)
{
try { OnConnected(); }
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
}
}
void RequestCompletedHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error)
{
// We don't care about this request now that it has completed
_Request = null;
OSDArray events = null;
int ack = 0;
if (responseData != null)
{
_errorCount = 0;
// Got a response
OSDMap result = OSDParser.DeserializeLLSDXml(responseData) as OSDMap;
if (result != null)
{
events = result["events"] as OSDArray;
ack = result["id"].AsInteger();
}
else
{
Logger.Log("Got an unparseable response from the event queue: \"" +
System.Text.Encoding.UTF8.GetString(responseData) + "\"", Helpers.LogLevel.Warning);
}
}
else if (error != null)
{
#region Error handling
HttpStatusCode code = HttpStatusCode.OK;
if (error is WebException)
{
WebException webException = (WebException)error;
if (webException.Response != null)
code = ((HttpWebResponse)webException.Response).StatusCode;
else if (webException.Status == WebExceptionStatus.RequestCanceled)
goto HandlingDone;
}
if (error is WebException && ((WebException)error).Response != null)
code = ((HttpWebResponse)((WebException)error).Response).StatusCode;
if (code == HttpStatusCode.NotFound || code == HttpStatusCode.Gone)
{
Logger.Log(String.Format("Closing event queue at {0} due to missing caps URI", _Address), Helpers.LogLevel.Info);
_Running = false;
_Dead = true;
}
else if (code == HttpStatusCode.BadGateway)
{
// This is not good (server) protocol design, but it's normal.
// The EventQueue server is a proxy that connects to a Squid
// cache which will time out periodically. The EventQueue server
// interprets this as a generic error and returns a 502 to us
// that we ignore
}
else
{
++_errorCount;
// Try to log a meaningful error message
if (code != HttpStatusCode.OK)
{
Logger.Log(String.Format("Unrecognized caps connection problem from {0}: {1}",
_Address, code), Helpers.LogLevel.Warning);
}
else if (error.InnerException != null)
{
Logger.Log(String.Format("Unrecognized internal caps exception from {0}: {1}",
_Address, error.InnerException.Message), Helpers.LogLevel.Warning);
}
else
{
Logger.Log(String.Format("Unrecognized caps exception from {0}: {1}",
_Address, error.Message), Helpers.LogLevel.Warning);
}
}
#endregion Error handling
}
else
{
++_errorCount;
Logger.Log("No response from the event queue but no reported error either", Helpers.LogLevel.Warning);
}
HandlingDone:
#region Resume the connection
if (_Running)
{
OSDMap osdRequest = new OSDMap();
if (ack != 0) osdRequest["ack"] = OSD.FromInteger(ack);
else osdRequest["ack"] = new OSD();
osdRequest["done"] = OSD.FromBoolean(_Dead);
byte[] postData = OSDParser.SerializeLLSDXmlBytes(osdRequest);
if (_errorCount > 0) // Exponentially back off, so we don't hammer the CPU
Thread.Sleep(_random.Next(500 + (int)Math.Pow(2, _errorCount)));
// Resume the connection. The event handler for the connection opening
// just sets class _Request variable to the current HttpWebRequest
CapsBase.UploadDataAsync(_Address, null, "application/xml", postData, REQUEST_TIMEOUT,
delegate(HttpWebRequest newRequest) { _Request = newRequest; }, null, RequestCompletedHandler);
// If the event queue is dead at this point, turn it off since
// that was the last thing we want to do
if (_Dead)
{
_Running = false;
Logger.DebugLog("Sent event queue shutdown message");
}
}
#endregion Resume the connection
#region Handle incoming events
if (OnEvent != null && events != null && events.Count > 0)
{
// Fire callbacks for each event received
foreach (OSDMap evt in events)
{
string msg = evt["message"].AsString();
OSDMap body = (OSDMap)evt["body"];
try { OnEvent(msg, body); }
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
}
}
#endregion Handle incoming events
}
}
}
+303
View File
@@ -0,0 +1,303 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using System.Text;
using System.Threading;
using OpenMetaverse.Packets;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Interfaces;
using OpenMetaverse.Http;
namespace OpenMetaverse
{
/// <summary>
/// Capabilities is the name of the bi-directional HTTP REST protocol
/// used to communicate non real-time transactions such as teleporting or
/// group messaging
/// </summary>
public partial class Caps
{
/// <summary>
/// Triggered when an event is received via the EventQueueGet
/// capability
/// </summary>
/// <param name="capsKey">Event name</param>
/// <param name="message">Decoded event data</param>
/// <param name="simulator">The simulator that generated the event</param>
//public delegate void EventQueueCallback(string message, StructuredData.OSD body, Simulator simulator);
public delegate void EventQueueCallback(string capsKey, IMessage message, Simulator simulator);
/// <summary>Reference to the simulator this system is connected to</summary>
public Simulator Simulator;
internal string _SeedCapsURI;
internal Dictionary<string, Uri> _Caps = new Dictionary<string, Uri>();
private CapsClient _SeedRequest;
private EventQueueClient _EventQueueCap = null;
/// <summary>Capabilities URI this system was initialized with</summary>
public string SeedCapsURI { get { return _SeedCapsURI; } }
/// <summary>Whether the capabilities event queue is connected and
/// listening for incoming events</summary>
public bool IsEventQueueRunning
{
get
{
if (_EventQueueCap != null)
return _EventQueueCap.Running;
else
return false;
}
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="simulator"></param>
/// <param name="seedcaps"></param>
internal Caps(Simulator simulator, string seedcaps)
{
Simulator = simulator;
_SeedCapsURI = seedcaps;
MakeSeedRequest();
}
public void Disconnect(bool immediate)
{
Logger.Log(String.Format("Caps system for {0} is {1}", Simulator,
(immediate ? "aborting" : "disconnecting")), Helpers.LogLevel.Info, Simulator.Client);
if (_SeedRequest != null)
_SeedRequest.Cancel();
if (_EventQueueCap != null)
_EventQueueCap.Stop(immediate);
}
/// <summary>
/// Request the URI of a named capability
/// </summary>
/// <param name="capability">Name of the capability to request</param>
/// <returns>The URI of the requested capability, or String.Empty if
/// the capability does not exist</returns>
public Uri CapabilityURI(string capability)
{
Uri cap;
if (_Caps.TryGetValue(capability, out cap))
return cap;
else
return null;
}
private void MakeSeedRequest()
{
if (Simulator == null || !Simulator.Client.Network.Connected)
return;
// Create a request list
OSDArray req = new OSDArray();
// This list can be updated by using the following command to obtain a current list of capabilities the official linden viewer supports:
// wget -q -O - https://bitbucket.org/lindenlab/viewer-release/raw/default/indra/newview/llviewerregion.cpp | grep 'capabilityNames.append' | sed 's/^[ \t]*//;s/capabilityNames.append("/req.Add("/'
req.Add("AgentPreferences");
req.Add("AgentState");
req.Add("AttachmentResources");
req.Add("AvatarPickerSearch");
req.Add("AvatarRenderInfo");
req.Add("CharacterProperties");
req.Add("ChatSessionRequest");
req.Add("CopyInventoryFromNotecard");
req.Add("CreateInventoryCategory");
req.Add("DispatchRegionInfo");
req.Add("EnvironmentSettings");
req.Add("EstateChangeInfo");
req.Add("EventQueueGet");
req.Add("FacebookConnect");
req.Add("FlickrConnect");
req.Add("TwitterConnect");
req.Add("FetchLib2");
req.Add("FetchLibDescendents2");
req.Add("FetchInventory2");
req.Add("FetchInventoryDescendents2");
req.Add("IncrementCOFVersion");
req.Add("GetDisplayNames");
req.Add("GetMesh");
req.Add("GetMesh2");
req.Add("GetObjectCost");
req.Add("GetObjectPhysicsData");
req.Add("GetTexture");
req.Add("GroupAPIv1");
req.Add("GroupMemberData");
req.Add("GroupProposalBallot");
req.Add("HomeLocation");
req.Add("LandResources");
req.Add("LSLSyntax");
req.Add("MapLayer");
req.Add("MapLayerGod");
req.Add("MeshUploadFlag");
req.Add("NavMeshGenerationStatus");
req.Add("NewFileAgentInventory");
req.Add("ObjectMedia");
req.Add("ObjectMediaNavigate");
req.Add("ObjectNavMeshProperties");
req.Add("ParcelPropertiesUpdate");
req.Add("ParcelVoiceInfoRequest");
req.Add("ProductInfoRequest");
req.Add("ProvisionVoiceAccountRequest");
req.Add("RemoteParcelRequest");
req.Add("RenderMaterials");
req.Add("RequestTextureDownload");
req.Add("ResourceCostSelected");
req.Add("RetrieveNavMeshSrc");
req.Add("SearchStatRequest");
req.Add("SearchStatTracking");
req.Add("SendPostcard");
req.Add("SendUserReport");
req.Add("SendUserReportWithScreenshot");
req.Add("ServerReleaseNotes");
req.Add("SetDisplayName");
req.Add("SimConsoleAsync");
req.Add("SimulatorFeatures");
req.Add("StartGroupProposal");
req.Add("TerrainNavMeshProperties");
req.Add("TextureStats");
req.Add("UntrustedSimulatorMessage");
req.Add("UpdateAgentInformation");
req.Add("UpdateAgentLanguage");
req.Add("UpdateAvatarAppearance");
req.Add("UpdateGestureAgentInventory");
req.Add("UpdateGestureTaskInventory");
req.Add("UpdateNotecardAgentInventory");
req.Add("UpdateNotecardTaskInventory");
req.Add("UpdateScriptAgent");
req.Add("UpdateScriptTask");
req.Add("UploadBakedTexture");
req.Add("ViewerMetrics");
req.Add("ViewerStartAuction");
req.Add("ViewerStats");
_SeedRequest = new CapsClient(new Uri(_SeedCapsURI));
_SeedRequest.OnComplete += new CapsClient.CompleteCallback(SeedRequestCompleteHandler);
_SeedRequest.BeginGetResponse(req, OSDFormat.Xml, Simulator.Client.Settings.CAPS_TIMEOUT);
}
private void SeedRequestCompleteHandler(CapsClient client, OSD result, Exception error)
{
if (result != null && result.Type == OSDType.Map)
{
OSDMap respTable = (OSDMap)result;
foreach (string cap in respTable.Keys)
{
_Caps[cap] = respTable[cap].AsUri();
}
if (_Caps.ContainsKey("EventQueueGet"))
{
Logger.DebugLog("Starting event queue for " + Simulator.ToString(), Simulator.Client);
_EventQueueCap = new EventQueueClient(_Caps["EventQueueGet"]);
_EventQueueCap.OnConnected += EventQueueConnectedHandler;
_EventQueueCap.OnEvent += EventQueueEventHandler;
_EventQueueCap.Start();
}
}
else if (
error != null &&
error is WebException &&
((WebException)error).Response != null &&
((HttpWebResponse)((WebException)error).Response).StatusCode == HttpStatusCode.NotFound)
{
// 404 error
Logger.Log("Seed capability returned a 404, capability system is aborting", Helpers.LogLevel.Error);
}
else
{
// The initial CAPS connection failed, try again
MakeSeedRequest();
}
}
private void EventQueueConnectedHandler()
{
Simulator.Client.Network.RaiseConnectedEvent(Simulator);
}
/// <summary>
/// Process any incoming events, check to see if we have a message created for the event,
/// </summary>
/// <param name="eventName"></param>
/// <param name="body"></param>
private void EventQueueEventHandler(string eventName, OSDMap body)
{
IMessage message = Messages.MessageUtils.DecodeEvent(eventName, body);
if (message != null)
{
Simulator.Client.Network.CapsEvents.BeginRaiseEvent(eventName, message, Simulator);
#region Stats Tracking
if (Simulator.Client.Settings.TRACK_UTILIZATION)
{
Simulator.Client.Stats.Update(eventName, OpenMetaverse.Stats.Type.Message, 0, body.ToString().Length);
}
#endregion
}
else
{
Logger.Log("No Message handler exists for event " + eventName + ". Unable to decode. Will try Generic Handler next", Helpers.LogLevel.Warning);
Logger.Log("Please report this information to http://jira.openmetaverse.org/: \n" + body, Helpers.LogLevel.Debug);
// try generic decoder next which takes a caps event and tries to match it to an existing packet
if (body.Type == OSDType.Map)
{
OSDMap map = (OSDMap)body;
Packet packet = Packet.BuildPacket(eventName, map);
if (packet != null)
{
NetworkManager.IncomingPacket incomingPacket;
incomingPacket.Simulator = Simulator;
incomingPacket.Packet = packet;
Logger.DebugLog("Serializing " + packet.Type.ToString() + " capability with generic handler", Simulator.Client);
Simulator.Client.Network.PacketInbox.Enqueue(incomingPacket);
}
else
{
Logger.Log("No Packet or Message handler exists for " + eventName, Helpers.LogLevel.Warning);
}
}
}
}
}
}
+293
View File
@@ -0,0 +1,293 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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;
using System.Collections.Generic;
using System.Reflection;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.Packets
{
public abstract partial class Packet
{
#region Serialization/Deserialization
public static string ToXmlString(Packet packet)
{
return OSDParser.SerializeLLSDXmlString(GetLLSD(packet));
}
public static OSD GetLLSD(Packet packet)
{
OSDMap body = new OSDMap();
Type type = packet.GetType();
foreach (FieldInfo field in type.GetFields())
{
if (field.IsPublic)
{
Type blockType = field.FieldType;
if (blockType.IsArray)
{
object blockArray = field.GetValue(packet);
Array array = (Array)blockArray;
OSDArray blockList = new OSDArray(array.Length);
IEnumerator ie = array.GetEnumerator();
while (ie.MoveNext())
{
object block = ie.Current;
blockList.Add(BuildLLSDBlock(block));
}
body[field.Name] = blockList;
}
else
{
object block = field.GetValue(packet);
body[field.Name] = BuildLLSDBlock(block);
}
}
}
return body;
}
public static byte[] ToBinary(Packet packet)
{
return OSDParser.SerializeLLSDBinary(GetLLSD(packet));
}
public static Packet FromXmlString(string xml)
{
System.Xml.XmlTextReader reader =
new System.Xml.XmlTextReader(new System.IO.MemoryStream(Utils.StringToBytes(xml)));
return FromLLSD(OSDParser.DeserializeLLSDXml(reader));
}
public static Packet FromLLSD(OSD osd)
{
// FIXME: Need the inverse of the reflection magic above done here
throw new NotImplementedException();
}
#endregion Serialization/Deserialization
/// <summary>
/// Attempts to convert an LLSD structure to a known Packet type
/// </summary>
/// <param name="capsEventName">Event name, this must match an actual
/// packet name for a Packet to be successfully built</param>
/// <param name="body">LLSD to convert to a Packet</param>
/// <returns>A Packet on success, otherwise null</returns>
public static Packet BuildPacket(string capsEventName, OSDMap body)
{
Assembly assembly = Assembly.GetExecutingAssembly();
// Check if we have a subclass of packet with the same name as this event
Type type = assembly.GetType("OpenMetaverse.Packets." + capsEventName + "Packet", false);
if (type == null)
return null;
Packet packet = null;
try
{
// Create an instance of the object
packet = (Packet)Activator.CreateInstance(type);
// Iterate over all of the fields in the packet class, looking for matches in the LLSD
foreach (FieldInfo field in type.GetFields())
{
if (body.ContainsKey(field.Name))
{
Type blockType = field.FieldType;
if (blockType.IsArray)
{
OSDArray array = (OSDArray)body[field.Name];
Type elementType = blockType.GetElementType();
object[] blockArray = (object[])Array.CreateInstance(elementType, array.Count);
for (int i = 0; i < array.Count; i++)
{
OSDMap map = (OSDMap)array[i];
blockArray[i] = ParseLLSDBlock(map, elementType);
}
field.SetValue(packet, blockArray);
}
else
{
OSDMap map = (OSDMap)((OSDArray)body[field.Name])[0];
field.SetValue(packet, ParseLLSDBlock(map, blockType));
}
}
}
}
catch (Exception)
{
//FIXME Logger.Log(e.Message, Helpers.LogLevel.Error, e);
}
return packet;
}
private static object ParseLLSDBlock(OSDMap blockData, Type blockType)
{
object block = Activator.CreateInstance(blockType);
// Iterate over each field and set the value if a match was found in the LLSD
foreach (FieldInfo field in blockType.GetFields())
{
if (blockData.ContainsKey(field.Name))
{
Type fieldType = field.FieldType;
if (fieldType == typeof(ulong))
{
// ulongs come in as a byte array, convert it manually here
byte[] bytes = blockData[field.Name].AsBinary();
ulong value = Utils.BytesToUInt64(bytes);
field.SetValue(block, value);
}
else if (fieldType == typeof(uint))
{
// uints come in as a byte array, convert it manually here
byte[] bytes = blockData[field.Name].AsBinary();
uint value = Utils.BytesToUInt(bytes);
field.SetValue(block, value);
}
else if (fieldType == typeof(ushort))
{
// Just need a bit of manual typecasting love here
field.SetValue(block, (ushort)blockData[field.Name].AsInteger());
}
else if (fieldType == typeof(byte))
{
// Just need a bit of manual typecasting love here
field.SetValue(block, (byte)blockData[field.Name].AsInteger());
}
else if (fieldType == typeof(sbyte))
{
field.SetValue(block, (sbyte)blockData[field.Name].AsInteger());
}
else if (fieldType == typeof(short))
{
field.SetValue(block, (short)blockData[field.Name].AsInteger());
}
else if (fieldType == typeof(string))
{
field.SetValue(block, blockData[field.Name].AsString());
}
else if (fieldType == typeof(bool))
{
field.SetValue(block, blockData[field.Name].AsBoolean());
}
else if (fieldType == typeof(float))
{
field.SetValue(block, (float)blockData[field.Name].AsReal());
}
else if (fieldType == typeof(double))
{
field.SetValue(block, blockData[field.Name].AsReal());
}
else if (fieldType == typeof(int))
{
field.SetValue(block, blockData[field.Name].AsInteger());
}
else if (fieldType == typeof(UUID))
{
field.SetValue(block, blockData[field.Name].AsUUID());
}
else if (fieldType == typeof(Vector3))
{
Vector3 vec = ((OSDArray)blockData[field.Name]).AsVector3();
field.SetValue(block, vec);
}
else if (fieldType == typeof(Vector4))
{
Vector4 vec = ((OSDArray)blockData[field.Name]).AsVector4();
field.SetValue(block, vec);
}
else if (fieldType == typeof(Quaternion))
{
Quaternion quat = ((OSDArray)blockData[field.Name]).AsQuaternion();
field.SetValue(block, quat);
}
else if (fieldType == typeof(byte[]) && blockData[field.Name].Type == OSDType.String)
{
field.SetValue(block, Utils.StringToBytes(blockData[field.Name]));
}
}
}
// Additional fields come as properties, Handle those as well.
foreach (PropertyInfo property in blockType.GetProperties())
{
if (blockData.ContainsKey(property.Name))
{
OSDType proptype = blockData[property.Name].Type;
MethodInfo set = property.GetSetMethod();
if (proptype.Equals(OSDType.Binary))
{
set.Invoke(block, new object[] { blockData[property.Name].AsBinary() });
}
else
set.Invoke(block, new object[] { Utils.StringToBytes(blockData[property.Name].AsString()) });
}
}
return block;
}
private static OSD BuildLLSDBlock(object block)
{
OSDMap map = new OSDMap();
Type blockType = block.GetType();
foreach (FieldInfo field in blockType.GetFields())
{
if (field.IsPublic)
map[field.Name] = OSD.FromObject(field.GetValue(block));
}
foreach (PropertyInfo property in blockType.GetProperties())
{
if (property.Name != "Length")
{
map[property.Name] = OSD.FromObject(property.GetValue(block, null));
}
}
return map;
}
}
}
+283
View File
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse
{
public class CoordinateFrame
{
public static readonly Vector3 X_AXIS = new Vector3(1f, 0f, 0f);
public static readonly Vector3 Y_AXIS = new Vector3(0f, 1f, 0f);
public static readonly Vector3 Z_AXIS = new Vector3(0f, 0f, 1f);
/// <summary>Origin position of this coordinate frame</summary>
public Vector3 Origin
{
get { return origin; }
set
{
if (!value.IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame.Origin assignment");
origin = value;
}
}
/// <summary>X axis of this coordinate frame, or Forward/At in grid terms</summary>
public Vector3 XAxis
{
get { return xAxis; }
set
{
if (!value.IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame.XAxis assignment");
xAxis = value;
}
}
/// <summary>Y axis of this coordinate frame, or Left in grid terms</summary>
public Vector3 YAxis
{
get { return yAxis; }
set
{
if (!value.IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame.YAxis assignment");
yAxis = value;
}
}
/// <summary>Z axis of this coordinate frame, or Up in grid terms</summary>
public Vector3 ZAxis
{
get { return zAxis; }
set
{
if (!value.IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame.ZAxis assignment");
zAxis = value;
}
}
protected Vector3 origin;
protected Vector3 xAxis;
protected Vector3 yAxis;
protected Vector3 zAxis;
#region Constructors
public CoordinateFrame(Vector3 origin)
{
this.origin = origin;
xAxis = X_AXIS;
yAxis = Y_AXIS;
zAxis = Z_AXIS;
if (!this.origin.IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame constructor");
}
public CoordinateFrame(Vector3 origin, Vector3 direction)
{
this.origin = origin;
LookDirection(direction);
if (!IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame constructor");
}
public CoordinateFrame(Vector3 origin, Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
{
this.origin = origin;
this.xAxis = xAxis;
this.yAxis = yAxis;
this.zAxis = zAxis;
if (!IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame constructor");
}
public CoordinateFrame(Vector3 origin, Matrix4 rotation)
{
this.origin = origin;
xAxis = rotation.AtAxis;
yAxis = rotation.LeftAxis;
zAxis = rotation.UpAxis;
if (!IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame constructor");
}
public CoordinateFrame(Vector3 origin, Quaternion rotation)
{
Matrix4 m = Matrix4.CreateFromQuaternion(rotation);
this.origin = origin;
xAxis = m.AtAxis;
yAxis = m.LeftAxis;
zAxis = m.UpAxis;
if (!IsFinite())
throw new ArgumentException("Non-finite in CoordinateFrame constructor");
}
#endregion Constructors
#region Public Methods
public void ResetAxes()
{
xAxis = X_AXIS;
yAxis = Y_AXIS;
zAxis = Z_AXIS;
}
public void Rotate(float angle, Vector3 rotationAxis)
{
Quaternion q = Quaternion.CreateFromAxisAngle(rotationAxis, angle);
Rotate(q);
}
public void Rotate(Quaternion q)
{
Matrix4 m = Matrix4.CreateFromQuaternion(q);
Rotate(m);
}
public void Rotate(Matrix4 m)
{
xAxis = Vector3.Transform(xAxis, m);
yAxis = Vector3.Transform(yAxis, m);
Orthonormalize();
if (!IsFinite())
throw new Exception("Non-finite in CoordinateFrame.Rotate()");
}
public void Roll(float angle)
{
Quaternion q = Quaternion.CreateFromAxisAngle(xAxis, angle);
Matrix4 m = Matrix4.CreateFromQuaternion(q);
Rotate(m);
if (!yAxis.IsFinite() || !zAxis.IsFinite())
throw new Exception("Non-finite in CoordinateFrame.Roll()");
}
public void Pitch(float angle)
{
Quaternion q = Quaternion.CreateFromAxisAngle(yAxis, angle);
Matrix4 m = Matrix4.CreateFromQuaternion(q);
Rotate(m);
if (!xAxis.IsFinite() || !zAxis.IsFinite())
throw new Exception("Non-finite in CoordinateFrame.Pitch()");
}
public void Yaw(float angle)
{
Quaternion q = Quaternion.CreateFromAxisAngle(zAxis, angle);
Matrix4 m = Matrix4.CreateFromQuaternion(q);
Rotate(m);
if (!xAxis.IsFinite() || !yAxis.IsFinite())
throw new Exception("Non-finite in CoordinateFrame.Yaw()");
}
public void LookDirection(Vector3 at)
{
LookDirection(at, Z_AXIS);
}
/// <summary>
///
/// </summary>
/// <param name="at">Looking direction, must be a normalized vector</param>
/// <param name="upDirection">Up direction, must be a normalized vector</param>
public void LookDirection(Vector3 at, Vector3 upDirection)
{
// The two parameters cannot be parallel
Vector3 left = Vector3.Cross(upDirection, at);
if (left == Vector3.Zero)
{
// Prevent left from being zero
at.X += 0.01f;
at.Normalize();
left = Vector3.Cross(upDirection, at);
}
left.Normalize();
xAxis = at;
yAxis = left;
zAxis = Vector3.Cross(at, left);
}
/// <summary>
/// Align the coordinate frame X and Y axis with a given rotation
/// around the Z axis in radians
/// </summary>
/// <param name="heading">Absolute rotation around the Z axis in
/// radians</param>
public void LookDirection(double heading)
{
yAxis.X = (float)Math.Cos(heading);
yAxis.Y = (float)Math.Sin(heading);
xAxis.X = (float)-Math.Sin(heading);
xAxis.Y = (float)Math.Cos(heading);
}
public void LookAt(Vector3 origin, Vector3 target)
{
LookAt(origin, target, new Vector3(0f, 0f, 1f));
}
public void LookAt(Vector3 origin, Vector3 target, Vector3 upDirection)
{
this.origin = origin;
Vector3 at = new Vector3(target - origin);
at.Normalize();
LookDirection(at, upDirection);
}
#endregion Public Methods
protected bool IsFinite()
{
if (xAxis.IsFinite() && yAxis.IsFinite() && zAxis.IsFinite())
return true;
else
return false;
}
protected void Orthonormalize()
{
// Make sure the axis are orthagonal and normalized
xAxis.Normalize();
yAxis -= xAxis * (xAxis * yAxis);
yAxis.Normalize();
zAxis = Vector3.Cross(xAxis, yAxis);
}
}
}
File diff suppressed because it is too large Load Diff
+260
View File
@@ -0,0 +1,260 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Linq;
using System.Text;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using OpenMetaverse.Http;
namespace OpenMetaverse
{
/// <summary>
/// Represends individual HTTP Download request
/// </summary>
public class DownloadRequest
{
/// <summary>URI of the item to fetch</summary>
public Uri Address;
/// <summary>Timout specified in milliseconds</summary>
public int MillisecondsTimeout;
/// <summary>Download progress callback</summary>
public CapsBase.DownloadProgressEventHandler DownloadProgressCallback;
/// <summary>Download completed callback</summary>
public CapsBase.RequestCompletedEventHandler CompletedCallback;
/// <summary>Accept the following content type</summary>
public string ContentType;
/// <summary>How many times will this request be retried</summary>
public int Retries = 5;
/// <summary>Current fetch attempt</summary>
public int Attempt = 0;
/// <summary>Default constructor</summary>
public DownloadRequest()
{
}
/// <summary>Constructor</summary>
public DownloadRequest(Uri address, int millisecondsTimeout,
string contentType,
CapsBase.DownloadProgressEventHandler downloadProgressCallback,
CapsBase.RequestCompletedEventHandler completedCallback)
{
this.Address = address;
this.MillisecondsTimeout = millisecondsTimeout;
this.DownloadProgressCallback = downloadProgressCallback;
this.CompletedCallback = completedCallback;
this.ContentType = contentType;
}
}
internal class ActiveDownload
{
public List<CapsBase.DownloadProgressEventHandler> ProgresHadlers = new List<CapsBase.DownloadProgressEventHandler>();
public List<CapsBase.RequestCompletedEventHandler> CompletedHandlers = new List<CapsBase.RequestCompletedEventHandler>();
public HttpWebRequest Request;
}
/// <summary>
/// Manages async HTTP downloads with a limit on maximum
/// concurrent downloads
/// </summary>
public class DownloadManager
{
Queue<DownloadRequest> queue = new Queue<DownloadRequest>();
Dictionary<string, ActiveDownload> activeDownloads = new Dictionary<string, ActiveDownload>();
X509Certificate2 m_ClientCert;
/// <summary>Maximum number of parallel downloads from a single endpoint</summary>
public int ParallelDownloads { get; set; }
/// <summary>Client certificate</summary>
public X509Certificate2 ClientCert
{
get { return m_ClientCert; }
set { m_ClientCert = value; }
}
/// <summary>Default constructor</summary>
public DownloadManager()
{
ParallelDownloads = 8;
}
/// <summary>Cleanup method</summary>
public virtual void Dispose()
{
lock (activeDownloads)
{
foreach (ActiveDownload download in activeDownloads.Values)
{
try
{
if (download.Request != null)
{
download.Request.Abort();
}
}
catch { }
}
activeDownloads.Clear();
}
}
/// <summary>Setup http download request</summary>
protected virtual HttpWebRequest SetupRequest(Uri address, string acceptHeader)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(address);
request.Method = "GET";
if (!string.IsNullOrEmpty(acceptHeader))
request.Accept = acceptHeader;
// Add the client certificate to the request if one was given
if (m_ClientCert != null)
request.ClientCertificates.Add(m_ClientCert);
// Leave idle connections to this endpoint open for up to 60 seconds
request.ServicePoint.MaxIdleTime = 0;
// Disable stupid Expect-100: Continue header
request.ServicePoint.Expect100Continue = false;
// Crank up the max number of connections per endpoint
if (request.ServicePoint.ConnectionLimit < Settings.MAX_HTTP_CONNECTIONS)
{
Logger.Log(string.Format("In DownloadManager.SetupRequest() setting conn limit for {0}:{1} to {2}", address.Host, address.Port, Settings.MAX_HTTP_CONNECTIONS), Helpers.LogLevel.Debug);
request.ServicePoint.ConnectionLimit = Settings.MAX_HTTP_CONNECTIONS;
}
return request;
}
/// <summary>Check the queue for pending work</summary>
private void EnqueuePending()
{
lock (queue)
{
if (queue.Count > 0)
{
int nr = 0;
lock (activeDownloads)
{
nr = activeDownloads.Count;
}
// Logger.DebugLog(nr.ToString() + " active downloads. Queued textures: " + queue.Count.ToString());
for (int i = nr; i < ParallelDownloads && queue.Count > 0; i++)
{
DownloadRequest item = queue.Dequeue();
lock (activeDownloads)
{
string addr = item.Address.ToString();
if (activeDownloads.ContainsKey(addr))
{
activeDownloads[addr].CompletedHandlers.Add(item.CompletedCallback);
if (item.DownloadProgressCallback != null)
{
activeDownloads[addr].ProgresHadlers.Add(item.DownloadProgressCallback);
}
}
else
{
ActiveDownload activeDownload = new ActiveDownload();
activeDownload.CompletedHandlers.Add(item.CompletedCallback);
if (item.DownloadProgressCallback != null)
{
activeDownload.ProgresHadlers.Add(item.DownloadProgressCallback);
}
Logger.DebugLog("Requesting " + item.Address.ToString());
activeDownload.Request = SetupRequest(item.Address, item.ContentType);
CapsBase.DownloadDataAsync(
activeDownload.Request,
item.MillisecondsTimeout,
(HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive) =>
{
foreach (CapsBase.DownloadProgressEventHandler handler in activeDownload.ProgresHadlers)
{
handler(request, response, bytesReceived, totalBytesToReceive);
}
},
(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error) =>
{
lock (activeDownloads) activeDownloads.Remove(addr);
if (error == null || item.Attempt >= item.Retries || (error != null && error.Message.Contains("404")))
{
foreach (CapsBase.RequestCompletedEventHandler handler in activeDownload.CompletedHandlers)
{
handler(request, response, responseData, error);
}
}
else
{
item.Attempt++;
Logger.Log(string.Format("Texture {0} HTTP download failed, trying again retry {1}/{2}",
item.Address, item.Attempt, item.Retries), Helpers.LogLevel.Warning);
lock (queue) queue.Enqueue(item);
}
EnqueuePending();
}
);
activeDownloads[addr] = activeDownload;
}
}
}
}
}
}
/// <summary>Enqueue a new HTTP download</summary>
public void QueueDownload(DownloadRequest req)
{
lock (activeDownloads)
{
string addr = req.Address.ToString();
if (activeDownloads.ContainsKey(addr))
{
activeDownloads[addr].CompletedHandlers.Add(req.CompletedCallback);
if (req.DownloadProgressCallback != null)
{
activeDownloads[addr].ProgresHadlers.Add(req.DownloadProgressCallback);
}
return;
}
}
lock (queue)
{
queue.Enqueue(req);
}
EnqueuePending();
}
}
}
File diff suppressed because it is too large Load Diff
+370
View File
@@ -0,0 +1,370 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using System.Threading;
using OpenMetaverse.Packets;
using OpenMetaverse.Messages.Linden;
using OpenMetaverse.Interfaces;
namespace OpenMetaverse
{
/// <summary>
/// Registers, unregisters, and fires events generated by incoming packets
/// </summary>
public class PacketEventDictionary
{
private sealed class PacketCallback
{
public EventHandler<PacketReceivedEventArgs> Callback;
public bool IsAsync;
public PacketCallback(EventHandler<PacketReceivedEventArgs> callback, bool isAsync)
{
Callback = callback;
IsAsync = isAsync;
}
}
/// <summary>
/// Object that is passed to worker threads in the ThreadPool for
/// firing packet callbacks
/// </summary>
private struct PacketCallbackWrapper
{
/// <summary>Callback to fire for this packet</summary>
public EventHandler<PacketReceivedEventArgs> Callback;
/// <summary>Reference to the simulator that this packet came from</summary>
public Simulator Simulator;
/// <summary>The packet that needs to be processed</summary>
public Packet Packet;
}
/// <summary>Reference to the GridClient object</summary>
public GridClient Client;
private Dictionary<PacketType, PacketCallback> _EventTable = new Dictionary<PacketType, PacketCallback>();
/// <summary>
/// Default constructor
/// </summary>
/// <param name="client"></param>
public PacketEventDictionary(GridClient client)
{
Client = client;
}
/// <summary>
/// Register an event handler
/// </summary>
/// <remarks>Use PacketType.Default to fire this event on every
/// incoming packet</remarks>
/// <param name="packetType">Packet type to register the handler for</param>
/// <param name="eventHandler">Callback to be fired</param>
/// <param name="isAsync">True if this callback should be ran
/// asynchronously, false to run it synchronous</param>
public void RegisterEvent(PacketType packetType, EventHandler<PacketReceivedEventArgs> eventHandler, bool isAsync)
{
lock (_EventTable)
{
PacketCallback callback;
if (_EventTable.TryGetValue(packetType, out callback))
{
callback.Callback += eventHandler;
callback.IsAsync = callback.IsAsync || isAsync;
}
else
{
callback = new PacketCallback(eventHandler, isAsync);
_EventTable[packetType] = callback;
}
}
}
/// <summary>
/// Unregister an event handler
/// </summary>
/// <param name="packetType">Packet type to unregister the handler for</param>
/// <param name="eventHandler">Callback to be unregistered</param>
public void UnregisterEvent(PacketType packetType, EventHandler<PacketReceivedEventArgs> eventHandler)
{
lock (_EventTable)
{
PacketCallback callback;
if (_EventTable.TryGetValue(packetType, out callback))
{
callback.Callback -= eventHandler;
if (callback.Callback == null || callback.Callback.GetInvocationList().Length == 0)
_EventTable.Remove(packetType);
}
}
}
/// <summary>
/// Fire the events registered for this packet type
/// </summary>
/// <param name="packetType">Incoming packet type</param>
/// <param name="packet">Incoming packet</param>
/// <param name="simulator">Simulator this packet was received from</param>
internal void RaiseEvent(PacketType packetType, Packet packet, Simulator simulator)
{
PacketCallback callback;
// Default handler first, if one exists
if (_EventTable.TryGetValue(PacketType.Default, out callback) && callback.Callback != null)
{
if (callback.IsAsync)
{
PacketCallbackWrapper wrapper;
wrapper.Callback = callback.Callback;
wrapper.Packet = packet;
wrapper.Simulator = simulator;
WorkPool.QueueUserWorkItem(ThreadPoolDelegate, wrapper);
}
else
{
try { callback.Callback(this, new PacketReceivedEventArgs(packet, simulator)); }
catch (Exception ex)
{
Logger.Log("Default packet event handler: " + ex.ToString(), Helpers.LogLevel.Error, Client);
}
}
}
if (_EventTable.TryGetValue(packetType, out callback) && callback.Callback != null)
{
if (callback.IsAsync)
{
PacketCallbackWrapper wrapper;
wrapper.Callback = callback.Callback;
wrapper.Packet = packet;
wrapper.Simulator = simulator;
WorkPool.QueueUserWorkItem(ThreadPoolDelegate, wrapper);
}
else
{
try { callback.Callback(this, new PacketReceivedEventArgs(packet, simulator)); }
catch (Exception ex)
{
Logger.Log("Packet event handler: " + ex.ToString(), Helpers.LogLevel.Error, Client);
}
}
return;
}
if (packetType != PacketType.Default && packetType != PacketType.PacketAck)
{
Logger.DebugLog("No handler registered for packet event " + packetType, Client);
}
}
private void ThreadPoolDelegate(Object state)
{
PacketCallbackWrapper wrapper = (PacketCallbackWrapper)state;
try
{
wrapper.Callback(this, new PacketReceivedEventArgs(wrapper.Packet, wrapper.Simulator));
}
catch (Exception ex)
{
Logger.Log("Async Packet Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client);
}
}
}
/// <summary>
/// Registers, unregisters, and fires events generated by the Capabilities
/// event queue
/// </summary>
public class CapsEventDictionary
{
/// <summary>
/// Object that is passed to worker threads in the ThreadPool for
/// firing CAPS callbacks
/// </summary>
private struct CapsCallbackWrapper
{
/// <summary>Callback to fire for this packet</summary>
public Caps.EventQueueCallback Callback;
/// <summary>Name of the CAPS event</summary>
public string CapsEvent;
/// <summary>Strongly typed decoded data</summary>
public IMessage Message;
/// <summary>Reference to the simulator that generated this event</summary>
public Simulator Simulator;
}
/// <summary>Reference to the GridClient object</summary>
public GridClient Client;
private Dictionary<string, Caps.EventQueueCallback> _EventTable =
new Dictionary<string, Caps.EventQueueCallback>();
private WaitCallback _ThreadPoolCallback;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="client">Reference to the GridClient object</param>
public CapsEventDictionary(GridClient client)
{
Client = client;
_ThreadPoolCallback = new WaitCallback(ThreadPoolDelegate);
}
/// <summary>
/// Register an new event handler for a capabilities event sent via the EventQueue
/// </summary>
/// <remarks>Use String.Empty to fire this event on every CAPS event</remarks>
/// <param name="capsEvent">Capability event name to register the
/// handler for</param>
/// <param name="eventHandler">Callback to fire</param>
public void RegisterEvent(string capsEvent, Caps.EventQueueCallback eventHandler)
{
// TODO: Should we add support for synchronous CAPS handlers?
lock (_EventTable)
{
if (_EventTable.ContainsKey(capsEvent))
_EventTable[capsEvent] += eventHandler;
else
_EventTable[capsEvent] = eventHandler;
}
}
/// <summary>
/// Unregister a previously registered capabilities handler
/// </summary>
/// <param name="capsEvent">Capability event name unregister the
/// handler for</param>
/// <param name="eventHandler">Callback to unregister</param>
public void UnregisterEvent(string capsEvent, Caps.EventQueueCallback eventHandler)
{
lock (_EventTable)
{
if (_EventTable.ContainsKey(capsEvent) && _EventTable[capsEvent] != null)
_EventTable[capsEvent] -= eventHandler;
}
}
/// <summary>
/// Fire the events registered for this event type synchronously
/// </summary>
/// <param name="capsEvent">Capability name</param>
/// <param name="message">Decoded event body</param>
/// <param name="simulator">Reference to the simulator that
/// generated this event</param>
internal void RaiseEvent(string capsEvent, IMessage message, Simulator simulator)
{
bool specialHandler = false;
Caps.EventQueueCallback callback;
// Default handler first, if one exists
if (_EventTable.TryGetValue(capsEvent, out callback))
{
if (callback != null)
{
try { callback(capsEvent, message, simulator); }
catch (Exception ex) { Logger.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); }
}
}
// Explicit handler next
if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null)
{
try { callback(capsEvent, message, simulator); }
catch (Exception ex) { Logger.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); }
specialHandler = true;
}
if (!specialHandler)
Logger.Log("Unhandled CAPS event " + capsEvent, Helpers.LogLevel.Warning, Client);
}
/// <summary>
/// Fire the events registered for this event type asynchronously
/// </summary>
/// <param name="capsEvent">Capability name</param>
/// <param name="message">Decoded event body</param>
/// <param name="simulator">Reference to the simulator that
/// generated this event</param>
internal void BeginRaiseEvent(string capsEvent, IMessage message, Simulator simulator)
{
bool specialHandler = false;
Caps.EventQueueCallback callback;
// Default handler first, if one exists
if (_EventTable.TryGetValue(String.Empty, out callback))
{
if (callback != null)
{
callback(capsEvent, message, simulator);
// CapsCallbackWrapper wrapper;
// wrapper.Callback = callback;
// wrapper.CapsEvent = capsEvent;
// wrapper.Message = message;
// wrapper.Simulator = simulator;
// WorkPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper);
}
}
// Explicit handler next
if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null)
{
callback(capsEvent, message, simulator);
// CapsCallbackWrapper wrapper;
// wrapper.Callback = callback;
// wrapper.CapsEvent = capsEvent;
// wrapper.Message = message;
// wrapper.Simulator = simulator;
// WorkPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper);
specialHandler = true;
}
if (!specialHandler)
Logger.Log("Unhandled CAPS event " + capsEvent, Helpers.LogLevel.Warning, Client);
}
private void ThreadPoolDelegate(Object state)
{
CapsCallbackWrapper wrapper = (CapsCallbackWrapper)state;
try
{
wrapper.Callback(wrapper.CapsEvent, wrapper.Message, wrapper.Simulator);
}
catch (Exception ex)
{
Logger.Log("Async CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client);
}
}
}
}
File diff suppressed because it is too large Load Diff
+147
View File
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse
{
/// <summary>
/// Main class to expose grid functionality to clients. All of the
/// classes needed for sending and receiving data are accessible through
/// this class.
/// </summary>
/// <example>
/// <code>
/// // Example minimum code required to instantiate class and
/// // connect to a simulator.
/// using System;
/// using System.Collections.Generic;
/// using System.Text;
/// using OpenMetaverse;
///
/// namespace FirstBot
/// {
/// class Bot
/// {
/// public static GridClient Client;
/// static void Main(string[] args)
/// {
/// Client = new GridClient(); // instantiates the GridClient class
/// // to the global Client object
/// // Login to Simulator
/// Client.Network.Login("FirstName", "LastName", "Password", "FirstBot", "1.0");
/// // Wait for a Keypress
/// Console.ReadLine();
/// // Logout of simulator
/// Client.Network.Logout();
/// }
/// }
/// }
/// </code>
/// </example>
public class GridClient
{
/// <summary>Networking subsystem</summary>
public NetworkManager Network;
/// <summary>Settings class including constant values and changeable
/// parameters for everything</summary>
public Settings Settings;
/// <summary>Parcel (subdivided simulator lots) subsystem</summary>
public ParcelManager Parcels;
/// <summary>Our own avatars subsystem</summary>
public AgentManager Self;
/// <summary>Other avatars subsystem</summary>
public AvatarManager Avatars;
/// <summary>Estate subsystem</summary>
public EstateTools Estate;
/// <summary>Friends list subsystem</summary>
public FriendsManager Friends;
/// <summary>Grid (aka simulator group) subsystem</summary>
public GridManager Grid;
/// <summary>Object subsystem</summary>
public ObjectManager Objects;
/// <summary>Group subsystem</summary>
public GroupManager Groups;
/// <summary>Asset subsystem</summary>
public AssetManager Assets;
/// <summary>Appearance subsystem</summary>
public AppearanceManager Appearance;
/// <summary>Inventory subsystem</summary>
public InventoryManager Inventory;
/// <summary>Directory searches including classifieds, people, land
/// sales, etc</summary>
public DirectoryManager Directory;
/// <summary>Handles land, wind, and cloud heightmaps</summary>
public TerrainManager Terrain;
/// <summary>Handles sound-related networking</summary>
public SoundManager Sound;
/// <summary>Throttling total bandwidth usage, or allocating bandwidth
/// for specific data stream types</summary>
public AgentThrottle Throttle;
public Stats.UtilizationStatistics Stats;
/// <summary>
/// Default constructor
/// </summary>
public GridClient()
{
// Initialise SmartThreadPool when using mono
if (Type.GetType("Mono.Runtime") != null)
{
WorkPool.Init(true);
}
// These are order-dependant
Network = new NetworkManager(this);
Settings = new Settings(this);
Parcels = new ParcelManager(this);
Self = new AgentManager(this);
Avatars = new AvatarManager(this);
Estate = new EstateTools(this);
Friends = new FriendsManager(this);
Grid = new GridManager(this);
Objects = new ObjectManager(this);
Groups = new GroupManager(this);
Assets = new AssetManager(this);
Appearance = new AppearanceManager(this);
Inventory = new InventoryManager(this);
Directory = new DirectoryManager(this);
Terrain = new TerrainManager(this);
Sound = new SoundManager(this);
Throttle = new AgentThrottle(this);
Stats = new OpenMetaverse.Stats.UtilizationStatistics();
}
/// <summary>
/// Return the full name of this instance
/// </summary>
/// <returns>Client avatars full name</returns>
public override string ToString()
{
return Self.Name;
}
}
}
+933
View File
@@ -0,0 +1,933 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Http;
using OpenMetaverse.Packets;
namespace OpenMetaverse
{
#region Enums
/// <summary>
/// Map layer request type
/// </summary>
public enum GridLayerType : uint
{
/// <summary>Objects and terrain are shown</summary>
Objects = 0,
/// <summary>Only the terrain is shown, no objects</summary>
Terrain = 1,
/// <summary>Overlay showing land for sale and for auction</summary>
LandForSale = 2
}
/// <summary>
/// Type of grid item, such as telehub, event, populator location, etc.
/// </summary>
public enum GridItemType : uint
{
/// <summary>Telehub</summary>
Telehub = 1,
/// <summary>PG rated event</summary>
PgEvent = 2,
/// <summary>Mature rated event</summary>
MatureEvent = 3,
/// <summary>Popular location</summary>
Popular = 4,
/// <summary>Locations of avatar groups in a region</summary>
AgentLocations = 6,
/// <summary>Land for sale</summary>
LandForSale = 7,
/// <summary>Classified ad</summary>
Classified = 8,
/// <summary>Adult rated event</summary>
AdultEvent = 9,
/// <summary>Adult land for sale</summary>
AdultLandForSale = 10
}
#endregion Enums
#region Structs
/// <summary>
/// Information about a region on the grid map
/// </summary>
public struct GridRegion
{
/// <summary>Sim X position on World Map</summary>
public int X;
/// <summary>Sim Y position on World Map</summary>
public int Y;
/// <summary>Sim Name (NOTE: In lowercase!)</summary>
public string Name;
/// <summary></summary>
public SimAccess Access;
/// <summary>Appears to always be zero (None)</summary>
public RegionFlags RegionFlags;
/// <summary>Sim's defined Water Height</summary>
public byte WaterHeight;
/// <summary></summary>
public byte Agents;
/// <summary>UUID of the World Map image</summary>
public UUID MapImageID;
/// <summary>Unique identifier for this region, a combination of the X
/// and Y position</summary>
public ulong RegionHandle;
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("{0} ({1}/{2}), Handle: {3}, MapImage: {4}, Access: {5}, Flags: {6}",
Name, X, Y, RegionHandle, MapImageID, Access, RegionFlags);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj is GridRegion)
return Equals((GridRegion)obj);
else
return false;
}
private bool Equals(GridRegion region)
{
return (this.X == region.X && this.Y == region.Y);
}
}
/// <summary>
/// Visual chunk of the grid map
/// </summary>
public struct GridLayer
{
public int Bottom;
public int Left;
public int Top;
public int Right;
public UUID ImageID;
public bool ContainsRegion(int x, int y)
{
return (x >= Left && x <= Right && y >= Bottom && y <= Top);
}
}
#endregion Structs
#region Map Item Classes
/// <summary>
/// Base class for Map Items
/// </summary>
public abstract class MapItem
{
/// <summary>The Global X position of the item</summary>
public uint GlobalX;
/// <summary>The Global Y position of the item</summary>
public uint GlobalY;
/// <summary>Get the Local X position of the item</summary>
public uint LocalX { get { return GlobalX % 256; } }
/// <summary>Get the Local Y position of the item</summary>
public uint LocalY { get { return GlobalY % 256; } }
/// <summary>Get the Handle of the region</summary>
public ulong RegionHandle
{
get { return Utils.UIntsToLong((uint)(GlobalX - (GlobalX % 256)), (uint)(GlobalY - (GlobalY % 256))); }
}
}
/// <summary>
/// Represents an agent or group of agents location
/// </summary>
public class MapAgentLocation : MapItem
{
public int AvatarCount;
public string Identifier;
}
/// <summary>
/// Represents a Telehub location
/// </summary>
public class MapTelehub : MapItem
{
}
/// <summary>
/// Represents a non-adult parcel of land for sale
/// </summary>
public class MapLandForSale : MapItem
{
public int Size;
public int Price;
public string Name;
public UUID ID;
}
/// <summary>
/// Represents an Adult parcel of land for sale
/// </summary>
public class MapAdultLandForSale : MapItem
{
public int Size;
public int Price;
public string Name;
public UUID ID;
}
/// <summary>
/// Represents a PG Event
/// </summary>
public class MapPGEvent : MapItem
{
public DirectoryManager.EventFlags Flags; // Extra
public DirectoryManager.EventCategories Category; // Extra2
public string Description;
}
/// <summary>
/// Represents a Mature event
/// </summary>
public class MapMatureEvent : MapItem
{
public DirectoryManager.EventFlags Flags; // Extra
public DirectoryManager.EventCategories Category; // Extra2
public string Description;
}
/// <summary>
/// Represents an Adult event
/// </summary>
public class MapAdultEvent : MapItem
{
public DirectoryManager.EventFlags Flags; // Extra
public DirectoryManager.EventCategories Category; // Extra2
public string Description;
}
#endregion Grid Item Classes
/// <summary>
/// Manages grid-wide tasks such as the world map
/// </summary>
public class GridManager
{
#region Delegates
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler<CoarseLocationUpdateEventArgs> m_CoarseLocationUpdate;
/// <summary>Raises the CoarseLocationUpdate event</summary>
/// <param name="e">A CoarseLocationUpdateEventArgs object containing the
/// data sent by simulator</param>
protected virtual void OnCoarseLocationUpdate(CoarseLocationUpdateEventArgs e)
{
EventHandler<CoarseLocationUpdateEventArgs> handler = m_CoarseLocationUpdate;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_CoarseLocationUpdateLock = new object();
/// <summary>Raised when the simulator sends a <see cref="CoarseLocationUpdatePacket"/>
/// containing the location of agents in the simulator</summary>
public event EventHandler<CoarseLocationUpdateEventArgs> CoarseLocationUpdate
{
add { lock (m_CoarseLocationUpdateLock) { m_CoarseLocationUpdate += value; } }
remove { lock (m_CoarseLocationUpdateLock) { m_CoarseLocationUpdate -= value; } }
}
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler<GridRegionEventArgs> m_GridRegion;
/// <summary>Raises the GridRegion event</summary>
/// <param name="e">A GridRegionEventArgs object containing the
/// data sent by simulator</param>
protected virtual void OnGridRegion(GridRegionEventArgs e)
{
EventHandler<GridRegionEventArgs> handler = m_GridRegion;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_GridRegionLock = new object();
/// <summary>Raised when the simulator sends a Region Data in response to
/// a Map request</summary>
public event EventHandler<GridRegionEventArgs> GridRegion
{
add { lock (m_GridRegionLock) { m_GridRegion += value; } }
remove { lock (m_GridRegionLock) { m_GridRegion -= value; } }
}
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler<GridLayerEventArgs> m_GridLayer;
/// <summary>Raises the GridLayer event</summary>
/// <param name="e">A GridLayerEventArgs object containing the
/// data sent by simulator</param>
protected virtual void OnGridLayer(GridLayerEventArgs e)
{
EventHandler<GridLayerEventArgs> handler = m_GridLayer;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_GridLayerLock = new object();
/// <summary>Raised when the simulator sends GridLayer object containing
/// a map tile coordinates and texture information</summary>
public event EventHandler<GridLayerEventArgs> GridLayer
{
add { lock (m_GridLayerLock) { m_GridLayer += value; } }
remove { lock (m_GridLayerLock) { m_GridLayer -= value; } }
}
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler<GridItemsEventArgs> m_GridItems;
/// <summary>Raises the GridItems event</summary>
/// <param name="e">A GridItemEventArgs object containing the
/// data sent by simulator</param>
protected virtual void OnGridItems(GridItemsEventArgs e)
{
EventHandler<GridItemsEventArgs> handler = m_GridItems;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_GridItemsLock = new object();
/// <summary>Raised when the simulator sends GridItems object containing
/// details on events, land sales at a specific location</summary>
public event EventHandler<GridItemsEventArgs> GridItems
{
add { lock (m_GridItemsLock) { m_GridItems += value; } }
remove { lock (m_GridItemsLock) { m_GridItems -= value; } }
}
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler<RegionHandleReplyEventArgs> m_RegionHandleReply;
/// <summary>Raises the RegionHandleReply event</summary>
/// <param name="e">A RegionHandleReplyEventArgs object containing the
/// data sent by simulator</param>
protected virtual void OnRegionHandleReply(RegionHandleReplyEventArgs e)
{
EventHandler<RegionHandleReplyEventArgs> handler = m_RegionHandleReply;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_RegionHandleReplyLock = new object();
/// <summary>Raised in response to a Region lookup</summary>
public event EventHandler<RegionHandleReplyEventArgs> RegionHandleReply
{
add { lock (m_RegionHandleReplyLock) { m_RegionHandleReply += value; } }
remove { lock (m_RegionHandleReplyLock) { m_RegionHandleReply -= value; } }
}
#endregion Delegates
/// <summary>Unknown</summary>
public float SunPhase { get { return sunPhase; } }
/// <summary>Current direction of the sun</summary>
public Vector3 SunDirection { get { return sunDirection; } }
/// <summary>Current angular velocity of the sun</summary>
public Vector3 SunAngVelocity { get { return sunAngVelocity; } }
/// <summary>Microseconds since the start of SL 4-hour day</summary>
public ulong TimeOfDay { get { return timeOfDay; } }
/// <summary>A dictionary of all the regions, indexed by region name</summary>
internal Dictionary<string, GridRegion> Regions = new Dictionary<string, GridRegion>();
/// <summary>A dictionary of all the regions, indexed by region handle</summary>
internal Dictionary<ulong, GridRegion> RegionsByHandle = new Dictionary<ulong, GridRegion>();
private GridClient Client;
private float sunPhase;
private Vector3 sunDirection;
private Vector3 sunAngVelocity;
private ulong timeOfDay;
/// <summary>
/// Constructor
/// </summary>
/// <param name="client">Instance of GridClient object to associate with this GridManager instance</param>
public GridManager(GridClient client)
{
Client = client;
//Client.Network.RegisterCallback(PacketType.MapLayerReply, MapLayerReplyHandler);
Client.Network.RegisterCallback(PacketType.MapBlockReply, MapBlockReplyHandler);
Client.Network.RegisterCallback(PacketType.MapItemReply, MapItemReplyHandler);
Client.Network.RegisterCallback(PacketType.SimulatorViewerTimeMessage, SimulatorViewerTimeMessageHandler);
Client.Network.RegisterCallback(PacketType.CoarseLocationUpdate, CoarseLocationHandler, false);
Client.Network.RegisterCallback(PacketType.RegionIDAndHandleReply, RegionHandleReplyHandler);
}
/// <summary>
///
/// </summary>
/// <param name="layer"></param>
public void RequestMapLayer(GridLayerType layer)
{
Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("MapLayer");
if (url != null)
{
OSDMap body = new OSDMap();
body["Flags"] = OSD.FromInteger((int)layer);
CapsClient request = new CapsClient(url);
request.OnComplete += new CapsClient.CompleteCallback(MapLayerResponseHandler);
request.BeginGetResponse(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
}
}
/// <summary>
/// Request a map layer
/// </summary>
/// <param name="regionName">The name of the region</param>
/// <param name="layer">The type of layer</param>
public void RequestMapRegion(string regionName, GridLayerType layer)
{
MapNameRequestPacket request = new MapNameRequestPacket();
request.AgentData.AgentID = Client.Self.AgentID;
request.AgentData.SessionID = Client.Self.SessionID;
request.AgentData.Flags = (uint)layer;
request.AgentData.EstateID = 0; // Filled in on the sim
request.AgentData.Godlike = false; // Filled in on the sim
request.NameData.Name = Utils.StringToBytes(regionName);
Client.Network.SendPacket(request);
}
/// <summary>
///
/// </summary>
/// <param name="layer"></param>
/// <param name="minX"></param>
/// <param name="minY"></param>
/// <param name="maxX"></param>
/// <param name="maxY"></param>
/// <param name="returnNonExistent"></param>
public void RequestMapBlocks(GridLayerType layer, ushort minX, ushort minY, ushort maxX, ushort maxY,
bool returnNonExistent)
{
MapBlockRequestPacket request = new MapBlockRequestPacket();
request.AgentData.AgentID = Client.Self.AgentID;
request.AgentData.SessionID = Client.Self.SessionID;
request.AgentData.Flags = (uint)layer;
request.AgentData.Flags |= (uint)(returnNonExistent ? 0x10000 : 0);
request.AgentData.EstateID = 0; // Filled in at the simulator
request.AgentData.Godlike = false; // Filled in at the simulator
request.PositionData.MinX = minX;
request.PositionData.MinY = minY;
request.PositionData.MaxX = maxX;
request.PositionData.MaxY = maxY;
Client.Network.SendPacket(request);
}
/// <summary>
///
/// </summary>
/// <param name="regionHandle"></param>
/// <param name="item"></param>
/// <param name="layer"></param>
/// <param name="timeoutMS"></param>
/// <returns></returns>
public List<MapItem> MapItems(ulong regionHandle, GridItemType item, GridLayerType layer, int timeoutMS)
{
List<MapItem> itemList = null;
AutoResetEvent itemsEvent = new AutoResetEvent(false);
EventHandler<GridItemsEventArgs> callback =
delegate(object sender, GridItemsEventArgs e)
{
if (e.Type == GridItemType.AgentLocations)
{
itemList = e.Items;
itemsEvent.Set();
}
};
GridItems += callback;
RequestMapItems(regionHandle, item, layer);
itemsEvent.WaitOne(timeoutMS, false);
GridItems -= callback;
return itemList;
}
/// <summary>
///
/// </summary>
/// <param name="regionHandle"></param>
/// <param name="item"></param>
/// <param name="layer"></param>
public void RequestMapItems(ulong regionHandle, GridItemType item, GridLayerType layer)
{
MapItemRequestPacket request = new MapItemRequestPacket();
request.AgentData.AgentID = Client.Self.AgentID;
request.AgentData.SessionID = Client.Self.SessionID;
request.AgentData.Flags = (uint)layer;
request.AgentData.Godlike = false; // Filled in on the sim
request.AgentData.EstateID = 0; // Filled in on the sim
request.RequestData.ItemType = (uint)item;
request.RequestData.RegionHandle = regionHandle;
Client.Network.SendPacket(request);
}
/// <summary>
/// Request data for all mainland (Linden managed) simulators
/// </summary>
public void RequestMainlandSims(GridLayerType layer)
{
RequestMapBlocks(layer, 0, 0, 65535, 65535, false);
}
/// <summary>
/// Request the region handle for the specified region UUID
/// </summary>
/// <param name="regionID">UUID of the region to look up</param>
public void RequestRegionHandle(UUID regionID)
{
RegionHandleRequestPacket request = new RegionHandleRequestPacket();
request.RequestBlock = new RegionHandleRequestPacket.RequestBlockBlock();
request.RequestBlock.RegionID = regionID;
Client.Network.SendPacket(request);
}
/// <summary>
/// Get grid region information using the region name, this function
/// will block until it can find the region or gives up
/// </summary>
/// <param name="name">Name of sim you're looking for</param>
/// <param name="layer">Layer that you are requesting</param>
/// <param name="region">Will contain a GridRegion for the sim you're
/// looking for if successful, otherwise an empty structure</param>
/// <returns>True if the GridRegion was successfully fetched, otherwise
/// false</returns>
public bool GetGridRegion(string name, GridLayerType layer, out GridRegion region)
{
if (String.IsNullOrEmpty(name))
{
Logger.Log("GetGridRegion called with a null or empty region name", Helpers.LogLevel.Error, Client);
region = new GridRegion();
return false;
}
if (Regions.ContainsKey(name))
{
// We already have this GridRegion structure
region = Regions[name];
return true;
}
else
{
AutoResetEvent regionEvent = new AutoResetEvent(false);
EventHandler<GridRegionEventArgs> callback =
delegate(object sender, GridRegionEventArgs e)
{
if (e.Region.Name == name)
regionEvent.Set();
};
GridRegion += callback;
RequestMapRegion(name, layer);
regionEvent.WaitOne(Client.Settings.MAP_REQUEST_TIMEOUT, false);
GridRegion -= callback;
if (Regions.ContainsKey(name))
{
// The region was found after our request
region = Regions[name];
return true;
}
else
{
Logger.Log("Couldn't find region " + name, Helpers.LogLevel.Warning, Client);
region = new GridRegion();
return false;
}
}
}
protected void MapLayerResponseHandler(CapsClient client, OSD result, Exception error)
{
if (result == null)
{
Logger.Log("MapLayerResponseHandler error: " + error.Message + ": " + error.StackTrace, Helpers.LogLevel.Error, Client);
return;
}
OSDMap body = (OSDMap)result;
OSDArray layerData = (OSDArray)body["LayerData"];
if (m_GridLayer != null)
{
for (int i = 0; i < layerData.Count; i++)
{
OSDMap thisLayerData = (OSDMap)layerData[i];
GridLayer layer;
layer.Bottom = thisLayerData["Bottom"].AsInteger();
layer.Left = thisLayerData["Left"].AsInteger();
layer.Top = thisLayerData["Top"].AsInteger();
layer.Right = thisLayerData["Right"].AsInteger();
layer.ImageID = thisLayerData["ImageID"].AsUUID();
OnGridLayer(new GridLayerEventArgs(layer));
}
}
if (body.ContainsKey("MapBlocks"))
{
// TODO: At one point this will become activated
Logger.Log("Got MapBlocks through CAPS, please finish this function!", Helpers.LogLevel.Error, Client);
}
}
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void MapBlockReplyHandler(object sender, PacketReceivedEventArgs e)
{
MapBlockReplyPacket map = (MapBlockReplyPacket)e.Packet;
foreach (MapBlockReplyPacket.DataBlock block in map.Data)
{
if (block.X != 0 || block.Y != 0)
{
GridRegion region;
region.X = block.X;
region.Y = block.Y;
region.Name = Utils.BytesToString(block.Name);
// RegionFlags seems to always be zero here?
region.RegionFlags = (RegionFlags)block.RegionFlags;
region.WaterHeight = block.WaterHeight;
region.Agents = block.Agents;
region.Access = (SimAccess)block.Access;
region.MapImageID = block.MapImageID;
region.RegionHandle = Utils.UIntsToLong((uint)(region.X * 256), (uint)(region.Y * 256));
lock (Regions)
{
Regions[region.Name] = region;
RegionsByHandle[region.RegionHandle] = region;
}
if (m_GridRegion != null)
{
OnGridRegion(new GridRegionEventArgs(region));
}
}
}
}
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void MapItemReplyHandler(object sender, PacketReceivedEventArgs e)
{
if (m_GridItems != null)
{
MapItemReplyPacket reply = (MapItemReplyPacket)e.Packet;
GridItemType type = (GridItemType)reply.RequestData.ItemType;
List<MapItem> items = new List<MapItem>();
for (int i = 0; i < reply.Data.Length; i++)
{
string name = Utils.BytesToString(reply.Data[i].Name);
switch (type)
{
case GridItemType.AgentLocations:
MapAgentLocation location = new MapAgentLocation();
location.GlobalX = reply.Data[i].X;
location.GlobalY = reply.Data[i].Y;
location.Identifier = name;
location.AvatarCount = reply.Data[i].Extra;
items.Add(location);
break;
case GridItemType.Classified:
//FIXME:
Logger.Log("FIXME", Helpers.LogLevel.Error, Client);
break;
case GridItemType.LandForSale:
MapLandForSale landsale = new MapLandForSale();
landsale.GlobalX = reply.Data[i].X;
landsale.GlobalY = reply.Data[i].Y;
landsale.ID = reply.Data[i].ID;
landsale.Name = name;
landsale.Size = reply.Data[i].Extra;
landsale.Price = reply.Data[i].Extra2;
items.Add(landsale);
break;
case GridItemType.MatureEvent:
MapMatureEvent matureEvent = new MapMatureEvent();
matureEvent.GlobalX = reply.Data[i].X;
matureEvent.GlobalY = reply.Data[i].Y;
matureEvent.Description = name;
matureEvent.Flags = (DirectoryManager.EventFlags)reply.Data[i].Extra2;
items.Add(matureEvent);
break;
case GridItemType.PgEvent:
MapPGEvent PGEvent = new MapPGEvent();
PGEvent.GlobalX = reply.Data[i].X;
PGEvent.GlobalY = reply.Data[i].Y;
PGEvent.Description = name;
PGEvent.Flags = (DirectoryManager.EventFlags)reply.Data[i].Extra2;
items.Add(PGEvent);
break;
case GridItemType.Popular:
//FIXME:
Logger.Log("FIXME", Helpers.LogLevel.Error, Client);
break;
case GridItemType.Telehub:
MapTelehub teleHubItem = new MapTelehub();
teleHubItem.GlobalX = reply.Data[i].X;
teleHubItem.GlobalY = reply.Data[i].Y;
items.Add(teleHubItem);
break;
case GridItemType.AdultLandForSale:
MapAdultLandForSale adultLandsale = new MapAdultLandForSale();
adultLandsale.GlobalX = reply.Data[i].X;
adultLandsale.GlobalY = reply.Data[i].Y;
adultLandsale.ID = reply.Data[i].ID;
adultLandsale.Name = name;
adultLandsale.Size = reply.Data[i].Extra;
adultLandsale.Price = reply.Data[i].Extra2;
items.Add(adultLandsale);
break;
case GridItemType.AdultEvent:
MapAdultEvent adultEvent = new MapAdultEvent();
adultEvent.GlobalX = reply.Data[i].X;
adultEvent.GlobalY = reply.Data[i].Y;
adultEvent.Description = Utils.BytesToString(reply.Data[i].Name);
adultEvent.Flags = (DirectoryManager.EventFlags)reply.Data[i].Extra2;
items.Add(adultEvent);
break;
default:
Logger.Log("Unknown map item type " + type, Helpers.LogLevel.Warning, Client);
break;
}
}
OnGridItems(new GridItemsEventArgs(type, items));
}
}
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void SimulatorViewerTimeMessageHandler(object sender, PacketReceivedEventArgs e)
{
SimulatorViewerTimeMessagePacket time = (SimulatorViewerTimeMessagePacket)e.Packet;
sunPhase = time.TimeInfo.SunPhase;
sunDirection = time.TimeInfo.SunDirection;
sunAngVelocity = time.TimeInfo.SunAngVelocity;
timeOfDay = time.TimeInfo.UsecSinceStart;
// TODO: Does anyone have a use for the time stuff?
}
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void CoarseLocationHandler(object sender, PacketReceivedEventArgs e)
{
CoarseLocationUpdatePacket coarse = (CoarseLocationUpdatePacket)e.Packet;
// populate a dictionary from the packet, for local use
Dictionary<UUID, Vector3> coarseEntries = new Dictionary<UUID, Vector3>();
for (int i = 0; i < coarse.AgentData.Length; i++)
{
if(coarse.Location.Length > 0)
coarseEntries[coarse.AgentData[i].AgentID] = new Vector3((int)coarse.Location[i].X, (int)coarse.Location[i].Y, (int)coarse.Location[i].Z * 4);
// the friend we are tracking on radar
if (i == coarse.Index.Prey)
e.Simulator.preyID = coarse.AgentData[i].AgentID;
}
// find stale entries (people who left the sim)
List<UUID> removedEntries = e.Simulator.avatarPositions.FindAll(delegate(UUID findID) { return !coarseEntries.ContainsKey(findID); });
// anyone who was not listed in the previous update
List<UUID> newEntries = new List<UUID>();
lock (e.Simulator.avatarPositions.Dictionary)
{
// remove stale entries
foreach(UUID trackedID in removedEntries)
e.Simulator.avatarPositions.Dictionary.Remove(trackedID);
// add or update tracked info, and record who is new
foreach (KeyValuePair<UUID, Vector3> entry in coarseEntries)
{
if (!e.Simulator.avatarPositions.Dictionary.ContainsKey(entry.Key))
newEntries.Add(entry.Key);
e.Simulator.avatarPositions.Dictionary[entry.Key] = entry.Value;
}
}
if (m_CoarseLocationUpdate != null)
{
WorkPool.QueueUserWorkItem(delegate(object o)
{ OnCoarseLocationUpdate(new CoarseLocationUpdateEventArgs(e.Simulator, newEntries, removedEntries)); });
}
}
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void RegionHandleReplyHandler(object sender, PacketReceivedEventArgs e)
{
if (m_RegionHandleReply != null)
{
RegionIDAndHandleReplyPacket reply = (RegionIDAndHandleReplyPacket)e.Packet;
OnRegionHandleReply(new RegionHandleReplyEventArgs(reply.ReplyBlock.RegionID, reply.ReplyBlock.RegionHandle));
}
}
}
#region EventArgs classes
public class CoarseLocationUpdateEventArgs : EventArgs
{
private readonly Simulator m_Simulator;
private readonly List<UUID> m_NewEntries;
private readonly List<UUID> m_RemovedEntries;
public Simulator Simulator { get { return m_Simulator; } }
public List<UUID> NewEntries { get { return m_NewEntries; } }
public List<UUID> RemovedEntries { get { return m_RemovedEntries; } }
public CoarseLocationUpdateEventArgs(Simulator simulator, List<UUID> newEntries, List<UUID> removedEntries)
{
this.m_Simulator = simulator;
this.m_NewEntries = newEntries;
this.m_RemovedEntries = removedEntries;
}
}
public class GridRegionEventArgs : EventArgs
{
private readonly GridRegion m_Region;
public GridRegion Region { get { return m_Region; } }
public GridRegionEventArgs(GridRegion region)
{
this.m_Region = region;
}
}
public class GridLayerEventArgs : EventArgs
{
private readonly GridLayer m_Layer;
public GridLayer Layer { get { return m_Layer; } }
public GridLayerEventArgs(GridLayer layer)
{
this.m_Layer = layer;
}
}
public class GridItemsEventArgs : EventArgs
{
private readonly GridItemType m_Type;
private readonly List<MapItem> m_Items;
public GridItemType Type { get { return m_Type; } }
public List<MapItem> Items { get { return m_Items; } }
public GridItemsEventArgs(GridItemType type, List<MapItem> items)
{
this.m_Type = type;
this.m_Items = items;
}
}
public class RegionHandleReplyEventArgs : EventArgs
{
private readonly UUID m_RegionID;
private readonly ulong m_RegionHandle;
public UUID RegionID { get { return m_RegionID; } }
public ulong RegionHandle { get { return m_RegionHandle; } }
public RegionHandleReplyEventArgs(UUID regionID, ulong regionHandle)
{
this.m_RegionID = regionID;
this.m_RegionHandle = regionHandle;
}
}
#endregion
}
File diff suppressed because it is too large Load Diff
+615
View File
@@ -0,0 +1,615 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using OpenMetaverse.Packets;
using System.IO;
using System.Reflection;
using OpenMetaverse.StructuredData;
using ComponentAce.Compression.Libs.zlib;
namespace OpenMetaverse
{
/// <summary>
/// Static helper functions and global variables
/// </summary>
public static class Helpers
{
/// <summary>This header flag signals that ACKs are appended to the packet</summary>
public const byte MSG_APPENDED_ACKS = 0x10;
/// <summary>This header flag signals that this packet has been sent before</summary>
public const byte MSG_RESENT = 0x20;
/// <summary>This header flags signals that an ACK is expected for this packet</summary>
public const byte MSG_RELIABLE = 0x40;
/// <summary>This header flag signals that the message is compressed using zerocoding</summary>
public const byte MSG_ZEROCODED = 0x80;
/// <summary>
/// Passed to Logger.Log() to identify the severity of a log entry
/// </summary>
public enum LogLevel
{
/// <summary>No logging information will be output</summary>
None,
/// <summary>Non-noisy useful information, may be helpful in
/// debugging a problem</summary>
Info,
/// <summary>A non-critical error occurred. A warning will not
/// prevent the rest of the library from operating as usual,
/// although it may be indicative of an underlying issue</summary>
Warning,
/// <summary>A critical error has occurred. Generally this will
/// be followed by the network layer shutting down, although the
/// stability of the library after an error is uncertain</summary>
Error,
/// <summary>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.
/// </summary>
Debug
};
/// <summary>
///
/// </summary>
/// <param name="offset"></param>
/// <returns></returns>
public static short TEOffsetShort(float offset)
{
offset = Utils.Clamp(offset, -1.0f, 1.0f);
offset *= 32767.0f;
return (short)Math.Round(offset);
}
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
/// <param name="pos"></param>
/// <returns></returns>
public static float TEOffsetFloat(byte[] bytes, int pos)
{
float offset = (float)BitConverter.ToInt16(bytes, pos);
return offset / 32767.0f;
}
/// <summary>
///
/// </summary>
/// <param name="rotation"></param>
/// <returns></returns>
public static short TERotationShort(float rotation)
{
const float TWO_PI = 6.283185307179586476925286766559f;
return (short)Math.Round(((Math.IEEERemainder(rotation, TWO_PI) / TWO_PI) * 32768.0f) + 0.5f);
}
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
/// <param name="pos"></param>
/// <returns></returns>
public static float TERotationFloat(byte[] bytes, int pos)
{
const float TWO_PI = 6.283185307179586476925286766559f;
return ((float)(bytes[pos] | (bytes[pos + 1] << 8)) / 32768.0f) * TWO_PI;
}
public static byte TEGlowByte(float glow)
{
return (byte)(glow * 255.0f);
}
public static float TEGlowFloat(byte[] bytes, int pos)
{
return (float)bytes[pos] / 255.0f;
}
/// <summary>
/// Given an X/Y location in absolute (grid-relative) terms, a region
/// handle is returned along with the local X/Y location in that region
/// </summary>
/// <param name="globalX">The absolute X location, a number such as
/// 255360.35</param>
/// <param name="globalY">The absolute Y location, a number such as
/// 255360.35</param>
/// <param name="localX">The sim-local X position of the global X
/// position, a value from 0.0 to 256.0</param>
/// <param name="localY">The sim-local Y position of the global Y
/// position, a value from 0.0 to 256.0</param>
/// <returns>A 64-bit region handle that can be used to teleport to</returns>
public static ulong GlobalPosToRegionHandle(float globalX, float globalY, out float localX, out float localY)
{
uint x = ((uint)globalX / 256) * 256;
uint y = ((uint)globalY / 256) * 256;
localX = globalX - (float)x;
localY = globalY - (float)y;
return Utils.UIntsToLong(x, y);
}
/// <summary>
/// Converts a floating point number to a terse string format used for
/// transmitting numbers in wearable asset files
/// </summary>
/// <param name="val">Floating point number to convert to a string</param>
/// <returns>A terse string representation of the input number</returns>
public static string FloatToTerseString(float val)
{
string s = string.Format(Utils.EnUsCulture, "{0:.00}", val);
if (val == 0)
return ".00";
// Trim trailing zeroes
while (s[s.Length - 1] == '0')
s = s.Remove(s.Length - 1, 1);
// Remove superfluous decimal places after the trim
if (s[s.Length - 1] == '.')
s = s.Remove(s.Length - 1, 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;
}
/// <summary>
/// Convert a variable length field (byte array) to a string, with a
/// field name prepended to each line of the output
/// </summary>
/// <remarks>If the byte array has unprintable characters in it, a
/// hex dump will be written instead</remarks>
/// <param name="output">The StringBuilder object to write to</param>
/// <param name="bytes">The byte array to convert to a string</param>
/// <param name="fieldName">A field name to prepend to each line of output</param>
internal static void FieldToString(StringBuilder output, byte[] bytes, string fieldName)
{
// Check for a common case
if (bytes.Length == 0) return;
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, 0, bytes.Length));
}
else
{
for (int i = 0; i < bytes.Length; i += 16)
{
if (i != 0)
output.Append('\n');
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(" ");
}
}
}
}
/// <summary>
/// Decode a zerocoded byte array, used to decompress packets marked
/// with the zerocoded flag
/// </summary>
/// <remarks>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.
/// </remarks>
/// <param name="src">The byte array to decode</param>
/// <param name="srclen">The length of the byte array to decode. This
/// would be the length of the packet up to (but not including) any
/// appended ACKs</param>
/// <param name="dest">The output byte array to decode to</param>
/// <returns>The length of the output buffer</returns>
public static int ZeroDecode(byte[] src, int srclen, byte[] dest)
{
if (srclen > src.Length)
throw new ArgumentException("srclen cannot be greater than src.Length");
uint zerolen = 0;
int bodylen = 0;
uint i = 0;
try
{
Buffer.BlockCopy(src, 0, dest, 0, 6);
zerolen = 6;
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 ex)
{
Logger.Log(String.Format("Zerodecoding error: i={0}, srclen={1}, bodylen={2}, zerolen={3}\n{4}\n{5}",
i, srclen, bodylen, zerolen, Utils.BytesToHexString(src, srclen, null), ex), LogLevel.Error);
throw new IndexOutOfRangeException(String.Format("Zerodecoding error: i={0}, srclen={1}, bodylen={2}, zerolen={3}\n{4}\n{5}",
i, srclen, bodylen, zerolen, Utils.BytesToHexString(src, srclen, null), ex.InnerException));
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="src">The byte array to encode</param>
/// <param name="srclen">The length of the byte array to encode</param>
/// <param name="dest">The output byte array to encode to</param>
/// <returns>The length of the output buffer</returns>
public static int ZeroEncode(byte[] src, int srclen, byte[] dest)
{
uint zerolen = 0;
byte zerocount = 0;
Buffer.BlockCopy(src, 0, dest, 0, 6);
zerolen += 6;
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;
}
/// <summary>
/// Calculates the CRC (cyclic redundancy check) needed to upload inventory.
/// </summary>
/// <param name="creationDate">Creation date</param>
/// <param name="saleType">Sale type</param>
/// <param name="invType">Inventory type</param>
/// <param name="type">Type</param>
/// <param name="assetID">Asset ID</param>
/// <param name="groupID">Group ID</param>
/// <param name="salePrice">Sale price</param>
/// <param name="ownerID">Owner ID</param>
/// <param name="creatorID">Creator ID</param>
/// <param name="itemID">Item ID</param>
/// <param name="folderID">Folder ID</param>
/// <param name="everyoneMask">Everyone mask (permissions)</param>
/// <param name="flags">Flags</param>
/// <param name="nextOwnerMask">Next owner mask (permissions)</param>
/// <param name="groupMask">Group mask (permissions)</param>
/// <param name="ownerMask">Owner mask (permissions)</param>
/// <returns>The calculated CRC</returns>
public static uint InventoryCRC(int creationDate, byte saleType, sbyte invType, sbyte type,
UUID assetID, UUID groupID, int salePrice, UUID ownerID, UUID creatorID,
UUID itemID, UUID 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 UUID 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;
}
/// <summary>
/// Attempts to load a file embedded in the assembly
/// </summary>
/// <param name="resourceName">The filename of the resource to load</param>
/// <returns>A Stream for the requested file, or null if the resource
/// was not successfully loaded</returns>
public static System.IO.Stream GetResourceStream(string resourceName)
{
return GetResourceStream(resourceName, "openmetaverse_data");
}
/// <summary>
/// Attempts to load a file either embedded in the assembly or found in
/// a given search path
/// </summary>
/// <param name="resourceName">The filename of the resource to load</param>
/// <param name="searchPath">An optional path that will be searched if
/// the asset is not found embedded in the assembly</param>
/// <returns>A Stream for the requested file, or null if the resource
/// was not successfully loaded</returns>
public static System.IO.Stream GetResourceStream(string resourceName, string searchPath)
{
if (searchPath != null)
{
Assembly gea = Assembly.GetEntryAssembly();
if (gea == null) gea = typeof(Helpers).Assembly;
string dirname = ".";
if (gea != null && gea.Location != null)
{
dirname = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(gea.Location), searchPath);
}
string filename = System.IO.Path.Combine(dirname, resourceName);
try
{
return new System.IO.FileStream(
filename,
System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
}
catch (Exception ex)
{
Logger.Log(string.Format("Failed opening resource from file {0}: {1}", filename, ex.Message), LogLevel.Error);
}
}
else
{
try
{
System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream s = a.GetManifestResourceStream("OpenMetaverse.Resources." + resourceName);
if (s != null) return s;
}
catch (Exception ex)
{
Logger.Log(string.Format("Failed opening resource stream: {0}", ex.Message), LogLevel.Error);
}
}
return null;
}
/// <summary>
/// Converts a list of primitives to an object that can be serialized
/// with the LLSD system
/// </summary>
/// <param name="prims">Primitives to convert to a serializable object</param>
/// <returns>An object that can be serialized with LLSD</returns>
public static StructuredData.OSD PrimListToOSD(List<Primitive> prims)
{
StructuredData.OSDMap map = new OpenMetaverse.StructuredData.OSDMap(prims.Count);
for (int i = 0; i < prims.Count; i++)
map.Add(prims[i].LocalID.ToString(), prims[i].GetOSD());
return map;
}
/// <summary>
/// Deserializes OSD in to a list of primitives
/// </summary>
/// <param name="osd">Structure holding the serialized primitive list,
/// must be of the SDMap type</param>
/// <returns>A list of deserialized primitives</returns>
public static List<Primitive> OSDToPrimList(StructuredData.OSD osd)
{
if (osd.Type != StructuredData.OSDType.Map)
throw new ArgumentException("LLSD must be in the Map structure");
StructuredData.OSDMap map = (StructuredData.OSDMap)osd;
List<Primitive> prims = new List<Primitive>(map.Count);
foreach (KeyValuePair<string, StructuredData.OSD> kvp in map)
{
Primitive prim = Primitive.FromOSD(kvp.Value);
prim.LocalID = UInt32.Parse(kvp.Key);
prims.Add(prim);
}
return prims;
}
/// <summary>
/// Converts a struct or class object containing fields only into a key value separated string
/// </summary>
/// <param name="t">The struct object</param>
/// <returns>A string containing the struct fields as the keys, and the field value as the value separated</returns>
/// <example>
/// <code>
/// // Add the following code to any struct or class containing only fields to override the ToString()
/// // method to display the values of the passed object
///
/// /// <summary>Print the struct data as a string</summary>
/// ///<returns>A string containing the field name, and field value</returns>
///public override string ToString()
///{
/// return Helpers.StructToString(this);
///}
/// </code>
/// </example>
public static string StructToString(object t)
{
StringBuilder result = new StringBuilder();
Type structType = t.GetType();
FieldInfo[] fields = structType.GetFields();
foreach (FieldInfo field in fields)
{
result.Append(field.Name + ": " + field.GetValue(t) + " ");
}
result.AppendLine();
return result.ToString().TrimEnd();
}
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[4096];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}
public static byte[] ZCompressOSD(OSD data)
{
byte[] ret = null;
using (MemoryStream outMemoryStream = new MemoryStream())
using (ZOutputStream outZStream = new ZOutputStream(outMemoryStream, zlibConst.Z_BEST_COMPRESSION))
using (Stream inMemoryStream = new MemoryStream(OSDParser.SerializeLLSDBinary(data, false)))
{
CopyStream(inMemoryStream, outZStream);
outZStream.finish();
ret = outMemoryStream.ToArray();
}
return ret;
}
public static OSD ZDecompressOSD(byte[] data)
{
OSD ret;
using (MemoryStream input = new MemoryStream(data))
using (MemoryStream output = new MemoryStream())
using (ZOutputStream zout = new ZOutputStream(output))
{
CopyStream(input, zout);
zout.finish();
output.Seek(0, SeekOrigin.Begin);
ret = OSDParser.DeserializeLLSDBinary(output);
}
return ret;
}
}
}
+647
View File
@@ -0,0 +1,647 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Drawing;
using OpenMetaverse.Assets;
namespace OpenMetaverse.Imaging
{
/// <summary>
/// A set of textures that are layered on texture of each other and "baked"
/// in to a single texture, for avatar appearances
/// </summary>
public class Baker
{
public static readonly UUID IMG_INVISIBLE = new UUID("3a367d1c-bef1-6d43-7595-e88c1e3aadb3");
#region Properties
/// <summary>Final baked texture</summary>
public AssetTexture BakedTexture { get { return bakedTexture; } }
/// <summary>Component layers</summary>
public List<AppearanceManager.TextureData> Textures { get { return textures; } }
/// <summary>Width of the final baked image and scratchpad</summary>
public int BakeWidth { get { return bakeWidth; } }
/// <summary>Height of the final baked image and scratchpad</summary>
public int BakeHeight { get { return bakeHeight; } }
/// <summary>Bake type</summary>
public BakeType BakeType { get { return bakeType; } }
/// <summary>Is this one of the 3 skin bakes</summary>
private bool IsSkin { get { return bakeType == BakeType.Head || bakeType == BakeType.LowerBody || bakeType == BakeType.UpperBody; } }
#endregion
#region Private fields
/// <summary>Final baked texture</summary>
private AssetTexture bakedTexture;
/// <summary>Component layers</summary>
private List<AppearanceManager.TextureData> textures = new List<AppearanceManager.TextureData>();
/// <summary>Width of the final baked image and scratchpad</summary>
private int bakeWidth;
/// <summary>Height of the final baked image and scratchpad</summary>
private int bakeHeight;
/// <summary>Bake type</summary>
private BakeType bakeType;
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
/// <param name="bakeType">Bake type</param>
public Baker(BakeType bakeType)
{
this.bakeType = bakeType;
if (bakeType == BakeType.Eyes)
{
bakeWidth = 128;
bakeHeight = 128;
}
else
{
bakeWidth = 512;
bakeHeight = 512;
}
}
#endregion
#region Public methods
/// <summary>
/// Adds layer for baking
/// </summary>
/// <param name="tdata">TexturaData struct that contains texture and its params</param>
public void AddTexture(AppearanceManager.TextureData tdata)
{
lock (textures)
{
textures.Add(tdata);
}
}
public void Bake()
{
bakedTexture = new AssetTexture(new ManagedImage(bakeWidth, bakeHeight,
ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump));
// Base color for eye bake is white, color of layer0 for others
if (bakeType == BakeType.Eyes)
{
InitBakedLayerColor(Color4.White);
}
else if (textures.Count > 0)
{
InitBakedLayerColor(textures[0].Color);
}
// Do we have skin texture?
bool SkinTexture = textures.Count > 0 && textures[0].Texture != null;
if (bakeType == BakeType.Head)
{
DrawLayer(LoadResourceLayer("head_color.tga"), false);
AddAlpha(bakedTexture.Image, LoadResourceLayer("head_alpha.tga"));
MultiplyLayerFromAlpha(bakedTexture.Image, LoadResourceLayer("head_skingrain.tga"));
}
if (!SkinTexture && bakeType == BakeType.UpperBody)
{
DrawLayer(LoadResourceLayer("upperbody_color.tga"), false);
}
if (!SkinTexture && bakeType == BakeType.LowerBody)
{
DrawLayer(LoadResourceLayer("lowerbody_color.tga"), false);
}
ManagedImage alphaWearableTexture = null;
// Layer each texture on top of one other, applying alpha masks as we go
for (int i = 0; i < textures.Count; i++)
{
// Skip if we have no texture on this layer
if (textures[i].Texture == null) continue;
// Is this Alpha wearable and does it have an alpha channel?
if (textures[i].TextureIndex >= AvatarTextureIndex.LowerAlpha &&
textures[i].TextureIndex <= AvatarTextureIndex.HairAlpha)
{
if (textures[i].Texture.Image.Alpha != null)
{
alphaWearableTexture = textures[i].Texture.Image.Clone();
}
else if (textures[i].TextureID == IMG_INVISIBLE)
{
alphaWearableTexture = new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Alpha);
}
continue;
}
// Don't draw skin and tattoo on head bake first
// For head bake the skin and texture are drawn last, go figure
if (bakeType == BakeType.Head && (i == 0 || i == 1)) continue;
ManagedImage texture = textures[i].Texture.Image.Clone();
//File.WriteAllBytes(bakeType + "-texture-layer-" + i + ".tga", texture.ExportTGA());
// Resize texture to the size of baked layer
// FIXME: if texture is smaller than the layer, don't stretch it, tile it
if (texture.Width != bakeWidth || texture.Height != bakeHeight)
{
try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); }
catch (Exception) { continue; }
}
// Special case for hair layer for the head bake
// If we don't have skin texture, we discard hair alpha
// and apply hair(i==2) pattern over the texture
if (!SkinTexture && bakeType == BakeType.Head && i == 2)
{
if (texture.Alpha != null)
{
for (int j = 0; j < texture.Alpha.Length; j++) texture.Alpha[j] = (byte)255;
}
MultiplyLayerFromAlpha(texture, LoadResourceLayer("head_hair.tga"));
}
// Aply tint and alpha masks except for skin that has a texture
// on layer 0 which always overrides other skin settings
if (!(IsSkin && i == 0))
{
ApplyTint(texture, textures[i].Color);
// For hair bake, we skip all alpha masks
// and use one from the texture, for both
// alpha and morph layers
if (bakeType == BakeType.Hair)
{
if (texture.Alpha != null)
{
bakedTexture.Image.Bump = texture.Alpha;
}
else
{
for (int j = 0; j < bakedTexture.Image.Bump.Length; j++) bakedTexture.Image.Bump[j] = byte.MaxValue;
}
}
// Apply parametrized alpha masks
else if (textures[i].AlphaMasks != null && textures[i].AlphaMasks.Count > 0)
{
// Combined mask for the layer, fully transparent to begin with
ManagedImage combinedMask = new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Alpha);
int addedMasks = 0;
// First add mask in normal blend mode
foreach (KeyValuePair<VisualAlphaParam, float> kvp in textures[i].AlphaMasks)
{
if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue;
if (kvp.Key.MultiplyBlend == false && (kvp.Value > 0f || !kvp.Key.SkipIfZero))
{
ApplyAlpha(combinedMask, kvp.Key, kvp.Value);
//File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA());
addedMasks++;
}
}
// If there were no mask in normal blend mode make aplha fully opaque
if (addedMasks == 0) for (int l = 0; l < combinedMask.Alpha.Length; l++) combinedMask.Alpha[l] = 255;
// Add masks in multiply blend mode
foreach (KeyValuePair<VisualAlphaParam, float> kvp in textures[i].AlphaMasks)
{
if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue;
if (kvp.Key.MultiplyBlend == true && (kvp.Value > 0f || !kvp.Key.SkipIfZero))
{
ApplyAlpha(combinedMask, kvp.Key, kvp.Value);
//File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA());
addedMasks++;
}
}
if (addedMasks > 0)
{
// Apply combined alpha mask to the cloned texture
AddAlpha(texture, combinedMask);
}
// Is this layer used for morph mask? If it is, use its
// alpha as the morth for the whole bake
if (Textures[i].TextureIndex == AppearanceManager.MorphLayerForBakeType(bakeType))
{
bakedTexture.Image.Bump = texture.Alpha;
}
//File.WriteAllBytes(bakeType + "-masked-texture-" + i + ".tga", texture.ExportTGA());
}
}
bool useAlpha = i == 0 && (BakeType == BakeType.Skirt || BakeType == BakeType.Hair);
DrawLayer(texture, useAlpha);
//File.WriteAllBytes(bakeType + "-layer-" + i + ".tga", texture.ExportTGA());
}
// For head and tattoo, we add skin last
if (IsSkin && bakeType == BakeType.Head)
{
ManagedImage texture;
if (textures[0].Texture != null)
{
texture = textures[0].Texture.Image.Clone();
if (texture.Width != bakeWidth || texture.Height != bakeHeight)
{
try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); }
catch (Exception) { }
}
DrawLayer(texture, false);
}
// Add head tattoo here (if available, order-dependant)
if (textures.Count > 1 && textures[1].Texture != null)
{
texture = textures[1].Texture.Image.Clone();
if (texture.Width != bakeWidth || texture.Height != bakeHeight)
{
try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); }
catch (Exception) { }
}
DrawLayer(texture, false);
}
}
// Apply any alpha wearable textures to make parts of the avatar disappear
if (alphaWearableTexture != null)
{
AddAlpha(bakedTexture.Image, alphaWearableTexture);
}
// We are done, encode asset for finalized bake
bakedTexture.Encode();
//File.WriteAllBytes(bakeType + ".tga", bakedTexture.Image.ExportTGA());
}
private static object ResourceSync = new object();
public static ManagedImage LoadResourceLayer(string fileName)
{
try
{
Bitmap bitmap = null;
lock (ResourceSync)
{
using (Stream stream = Helpers.GetResourceStream(fileName, Settings.RESOURCE_DIR))
{
bitmap = LoadTGAClass.LoadTGA(stream);
}
}
if (bitmap == null)
{
Logger.Log(String.Format("Failed loading resource file: {0}", fileName), Helpers.LogLevel.Error);
return null;
}
else
{
ManagedImage image = new ManagedImage(bitmap);
bitmap.Dispose();
return image;
}
}
catch (Exception e)
{
Logger.Log(String.Format("Failed loading resource file: {0} ({1})", fileName, e.Message),
Helpers.LogLevel.Error, e);
return null;
}
}
/// <summary>
/// Converts avatar texture index (face) to Bake type
/// </summary>
/// <param name="index">Face number (AvatarTextureIndex)</param>
/// <returns>BakeType, layer to which this texture belongs to</returns>
public static BakeType BakeTypeFor(AvatarTextureIndex index)
{
switch (index)
{
case AvatarTextureIndex.HeadBodypaint:
return BakeType.Head;
case AvatarTextureIndex.UpperBodypaint:
case AvatarTextureIndex.UpperGloves:
case AvatarTextureIndex.UpperUndershirt:
case AvatarTextureIndex.UpperShirt:
case AvatarTextureIndex.UpperJacket:
return BakeType.UpperBody;
case AvatarTextureIndex.LowerBodypaint:
case AvatarTextureIndex.LowerUnderpants:
case AvatarTextureIndex.LowerSocks:
case AvatarTextureIndex.LowerShoes:
case AvatarTextureIndex.LowerPants:
case AvatarTextureIndex.LowerJacket:
return BakeType.LowerBody;
case AvatarTextureIndex.EyesIris:
return BakeType.Eyes;
case AvatarTextureIndex.Skirt:
return BakeType.Skirt;
case AvatarTextureIndex.Hair:
return BakeType.Hair;
default:
return BakeType.Unknown;
}
}
#endregion
#region Private layer compositing methods
private bool MaskBelongsToBake(string mask)
{
if ((bakeType == BakeType.LowerBody && mask.Contains("upper"))
|| (bakeType == BakeType.LowerBody && mask.Contains("shirt"))
|| (bakeType == BakeType.UpperBody && mask.Contains("lower")))
{
return false;
}
else
{
return true;
}
}
private bool DrawLayer(ManagedImage source, bool addSourceAlpha)
{
if (source == null) return false;
bool sourceHasColor;
bool sourceHasAlpha;
bool sourceHasBump;
int i = 0;
sourceHasColor = ((source.Channels & ManagedImage.ImageChannels.Color) != 0 &&
source.Red != null && source.Green != null && source.Blue != null);
sourceHasAlpha = ((source.Channels & ManagedImage.ImageChannels.Alpha) != 0 && source.Alpha != null);
sourceHasBump = ((source.Channels & ManagedImage.ImageChannels.Bump) != 0 && source.Bump != null);
addSourceAlpha = (addSourceAlpha && sourceHasAlpha);
byte alpha = Byte.MaxValue;
byte alphaInv = (byte)(Byte.MaxValue - alpha);
byte[] bakedRed = bakedTexture.Image.Red;
byte[] bakedGreen = bakedTexture.Image.Green;
byte[] bakedBlue = bakedTexture.Image.Blue;
byte[] bakedAlpha = bakedTexture.Image.Alpha;
byte[] bakedBump = bakedTexture.Image.Bump;
byte[] sourceRed = source.Red;
byte[] sourceGreen = source.Green;
byte[] sourceBlue = source.Blue;
byte[] sourceAlpha = sourceHasAlpha ? source.Alpha : null;
byte[] sourceBump = sourceHasBump ? source.Bump : null;
for (int y = 0; y < bakeHeight; y++)
{
for (int x = 0; x < bakeWidth; x++)
{
if (sourceHasAlpha)
{
alpha = sourceAlpha[i];
alphaInv = (byte)(Byte.MaxValue - alpha);
}
if (sourceHasColor)
{
bakedRed[i] = (byte)((bakedRed[i] * alphaInv + sourceRed[i] * alpha) >> 8);
bakedGreen[i] = (byte)((bakedGreen[i] * alphaInv + sourceGreen[i] * alpha) >> 8);
bakedBlue[i] = (byte)((bakedBlue[i] * alphaInv + sourceBlue[i] * alpha) >> 8);
}
if (addSourceAlpha)
{
if (sourceAlpha[i] < bakedAlpha[i])
{
bakedAlpha[i] = sourceAlpha[i];
}
}
if (sourceHasBump)
bakedBump[i] = sourceBump[i];
++i;
}
}
return true;
}
/// <summary>
/// Make sure images exist, resize source if needed to match the destination
/// </summary>
/// <param name="dest">Destination image</param>
/// <param name="src">Source image</param>
/// <returns>Sanitization was succefull</returns>
private bool SanitizeLayers(ManagedImage dest, ManagedImage src)
{
if (dest == null || src == null) return false;
if ((dest.Channels & ManagedImage.ImageChannels.Alpha) == 0)
{
dest.ConvertChannels(dest.Channels | ManagedImage.ImageChannels.Alpha);
}
if (dest.Width != src.Width || dest.Height != src.Height)
{
try { src.ResizeNearestNeighbor(dest.Width, dest.Height); }
catch (Exception) { return false; }
}
return true;
}
private void ApplyAlpha(ManagedImage dest, VisualAlphaParam param, float val)
{
ManagedImage src = LoadResourceLayer(param.TGAFile);
if (dest == null || src == null || src.Alpha == null) return;
if ((dest.Channels & ManagedImage.ImageChannels.Alpha) == 0)
{
dest.ConvertChannels(ManagedImage.ImageChannels.Alpha | dest.Channels);
}
if (dest.Width != src.Width || dest.Height != src.Height)
{
try { src.ResizeNearestNeighbor(dest.Width, dest.Height); }
catch (Exception) { return; }
}
for (int i = 0; i < dest.Alpha.Length; i++)
{
byte alpha = src.Alpha[i] <= ((1 - val) * 255) ? (byte)0 : (byte)255;
if (alpha != 255)
{
}
if (param.MultiplyBlend)
{
dest.Alpha[i] = (byte)((dest.Alpha[i] * alpha) >> 8);
}
else
{
if (alpha > dest.Alpha[i])
{
dest.Alpha[i] = alpha;
}
}
}
}
private void AddAlpha(ManagedImage dest, ManagedImage src)
{
if (!SanitizeLayers(dest, src)) return;
for (int i = 0; i < dest.Alpha.Length; i++)
{
if (src.Alpha[i] < dest.Alpha[i])
{
dest.Alpha[i] = src.Alpha[i];
}
}
}
private void MultiplyLayerFromAlpha(ManagedImage dest, ManagedImage src)
{
if (!SanitizeLayers(dest, src)) return;
for (int i = 0; i < dest.Red.Length; i++)
{
dest.Red[i] = (byte)((dest.Red[i] * src.Alpha[i]) >> 8);
dest.Green[i] = (byte)((dest.Green[i] * src.Alpha[i]) >> 8);
dest.Blue[i] = (byte)((dest.Blue[i] * src.Alpha[i]) >> 8);
}
}
private void ApplyTint(ManagedImage dest, Color4 src)
{
if (dest == null) return;
for (int i = 0; i < dest.Red.Length; i++)
{
dest.Red[i] = (byte)((dest.Red[i] * ((byte)(src.R * byte.MaxValue))) >> 8);
dest.Green[i] = (byte)((dest.Green[i] * ((byte)(src.G * byte.MaxValue))) >> 8);
dest.Blue[i] = (byte)((dest.Blue[i] * ((byte)(src.B * byte.MaxValue))) >> 8);
}
}
/// <summary>
/// Fills a baked layer as a solid *appearing* color. The colors are
/// subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from
/// compressing it too far since it seems to cause upload failures if
/// the image is a pure solid color
/// </summary>
/// <param name="color">Color of the base of this layer</param>
private void InitBakedLayerColor(Color4 color)
{
InitBakedLayerColor(color.R, color.G, color.B);
}
/// <summary>
/// Fills a baked layer as a solid *appearing* color. The colors are
/// subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from
/// compressing it too far since it seems to cause upload failures if
/// the image is a pure solid color
/// </summary>
/// <param name="r">Red value</param>
/// <param name="g">Green value</param>
/// <param name="b">Blue value</param>
private void InitBakedLayerColor(float r, float g, float b)
{
byte rByte = Utils.FloatToByte(r, 0f, 1f);
byte gByte = Utils.FloatToByte(g, 0f, 1f);
byte bByte = Utils.FloatToByte(b, 0f, 1f);
byte rAlt, gAlt, bAlt;
rAlt = rByte;
gAlt = gByte;
bAlt = bByte;
if (rByte < Byte.MaxValue)
rAlt++;
else rAlt--;
if (gByte < Byte.MaxValue)
gAlt++;
else gAlt--;
if (bByte < Byte.MaxValue)
bAlt++;
else bAlt--;
int i = 0;
byte[] red = bakedTexture.Image.Red;
byte[] green = bakedTexture.Image.Green;
byte[] blue = bakedTexture.Image.Blue;
byte[] alpha = bakedTexture.Image.Alpha;
byte[] bump = bakedTexture.Image.Bump;
for (int y = 0; y < bakeHeight; y++)
{
for (int x = 0; x < bakeWidth; x++)
{
if (((x ^ y) & 0x10) == 0)
{
red[i] = rAlt;
green[i] = gByte;
blue[i] = bByte;
alpha[i] = Byte.MaxValue;
bump[i] = 0;
}
else
{
red[i] = rByte;
green[i] = gAlt;
blue[i] = bAlt;
alpha[i] = Byte.MaxValue;
bump[i] = 0;
}
++i;
}
}
}
#endregion
}
}
+535
View File
@@ -0,0 +1,535 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse.Imaging
{
public class ManagedImage
{
[Flags]
public enum ImageChannels
{
Gray = 1,
Color = 2,
Alpha = 4,
Bump = 8
};
public enum ImageResizeAlgorithm
{
NearestNeighbor
}
/// <summary>
/// Image width
/// </summary>
public int Width;
/// <summary>
/// Image height
/// </summary>
public int Height;
/// <summary>
/// Image channel flags
/// </summary>
public ImageChannels Channels;
/// <summary>
/// Red channel data
/// </summary>
public byte[] Red;
/// <summary>
/// Green channel data
/// </summary>
public byte[] Green;
/// <summary>
/// Blue channel data
/// </summary>
public byte[] Blue;
/// <summary>
/// Alpha channel data
/// </summary>
public byte[] Alpha;
/// <summary>
/// Bump channel data
/// </summary>
public byte[] Bump;
/// <summary>
/// Create a new blank image
/// </summary>
/// <param name="width">width</param>
/// <param name="height">height</param>
/// <param name="channels">channel flags</param>
public ManagedImage(int width, int height, ImageChannels channels)
{
Width = width;
Height = height;
Channels = channels;
int n = width * height;
if ((channels & ImageChannels.Gray) != 0)
{
Red = new byte[n];
}
else if ((channels & ImageChannels.Color) != 0)
{
Red = new byte[n];
Green = new byte[n];
Blue = new byte[n];
}
if ((channels & ImageChannels.Alpha) != 0)
Alpha = new byte[n];
if ((channels & ImageChannels.Bump) != 0)
Bump = new byte[n];
}
#if !NO_UNSAFE
/// <summary>
///
/// </summary>
/// <param name="bitmap"></param>
public ManagedImage(System.Drawing.Bitmap bitmap)
{
Width = bitmap.Width;
Height = bitmap.Height;
int pixelCount = Width * Height;
if (bitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
Channels = ImageChannels.Alpha | ImageChannels.Color;
Red = new byte[pixelCount];
Green = new byte[pixelCount];
Blue = new byte[pixelCount];
Alpha = new byte[pixelCount];
System.Drawing.Imaging.BitmapData bd = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, Width, Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
unsafe
{
byte* pixel = (byte*)bd.Scan0;
for (int i = 0; i < pixelCount; i++)
{
// GDI+ gives us BGRA and we need to turn that in to RGBA
Blue[i] = *(pixel++);
Green[i] = *(pixel++);
Red[i] = *(pixel++);
Alpha[i] = *(pixel++);
}
}
bitmap.UnlockBits(bd);
}
else if (bitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format16bppGrayScale)
{
Channels = ImageChannels.Gray;
Red = new byte[pixelCount];
throw new NotImplementedException("16bpp grayscale image support is incomplete");
}
else if (bitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
Channels = ImageChannels.Color;
Red = new byte[pixelCount];
Green = new byte[pixelCount];
Blue = new byte[pixelCount];
System.Drawing.Imaging.BitmapData bd = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, Width, Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
unsafe
{
byte* pixel = (byte*)bd.Scan0;
for (int i = 0; i < pixelCount; i++)
{
// GDI+ gives us BGR and we need to turn that in to RGB
Blue[i] = *(pixel++);
Green[i] = *(pixel++);
Red[i] = *(pixel++);
}
}
bitmap.UnlockBits(bd);
}
else if (bitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppRgb)
{
Channels = ImageChannels.Color;
Red = new byte[pixelCount];
Green = new byte[pixelCount];
Blue = new byte[pixelCount];
System.Drawing.Imaging.BitmapData bd = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, Width, Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
unsafe
{
byte* pixel = (byte*)bd.Scan0;
for (int i = 0; i < pixelCount; i++)
{
// GDI+ gives us BGR and we need to turn that in to RGB
Blue[i] = *(pixel++);
Green[i] = *(pixel++);
Red[i] = *(pixel++);
pixel++; // Skip over the empty byte where the Alpha info would normally be
}
}
bitmap.UnlockBits(bd);
}
else
{
throw new NotSupportedException("Unrecognized pixel format: " + bitmap.PixelFormat.ToString());
}
}
#endif
/// <summary>
/// Convert the channels in the image. Channels are created or destroyed as required.
/// </summary>
/// <param name="channels">new channel flags</param>
public void ConvertChannels(ImageChannels channels)
{
if (Channels == channels)
return;
int n = Width * Height;
ImageChannels add = Channels ^ channels & channels;
ImageChannels del = Channels ^ channels & Channels;
if ((add & ImageChannels.Color) != 0)
{
Red = new byte[n];
Green = new byte[n];
Blue = new byte[n];
}
else if ((del & ImageChannels.Color) != 0)
{
Red = null;
Green = null;
Blue = null;
}
if ((add & ImageChannels.Alpha) != 0)
{
Alpha = new byte[n];
FillArray(Alpha, 255);
}
else if ((del & ImageChannels.Alpha) != 0)
Alpha = null;
if ((add & ImageChannels.Bump) != 0)
Bump = new byte[n];
else if ((del & ImageChannels.Bump) != 0)
Bump = null;
Channels = channels;
}
/// <summary>
/// Resize or stretch the image using nearest neighbor (ugly) resampling
/// </summary>
/// <param name="width">new width</param>
/// <param name="height">new height</param>
public void ResizeNearestNeighbor(int width, int height)
{
if (width == Width && height == Height)
return;
byte[]
red = null,
green = null,
blue = null,
alpha = null,
bump = null;
int n = width * height;
int di = 0, si;
if (Red != null) red = new byte[n];
if (Green != null) green = new byte[n];
if (Blue != null) blue = new byte[n];
if (Alpha != null) alpha = new byte[n];
if (Bump != null) bump = new byte[n];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
si = (y * Height / height) * Width + (x * Width / width);
if (Red != null) red[di] = Red[si];
if (Green != null) green[di] = Green[si];
if (Blue != null) blue[di] = Blue[si];
if (Alpha != null) alpha[di] = Alpha[si];
if (Bump != null) bump[di] = Bump[si];
di++;
}
}
Width = width;
Height = height;
Red = red;
Green = green;
Blue = blue;
Alpha = alpha;
Bump = bump;
}
/// <summary>
/// Create a byte array containing 32-bit RGBA data with a bottom-left
/// origin, suitable for feeding directly into OpenGL
/// </summary>
/// <returns>A byte array containing raw texture data</returns>
public byte[] ExportRaw()
{
byte[] raw = new byte[Width * Height * 4];
if ((Channels & ImageChannels.Alpha) != 0)
{
if ((Channels & ImageChannels.Color) != 0)
{
// RGBA
for (int h = 0; h < Height; h++)
{
for (int w = 0; w < Width; w++)
{
int pos = (Height - 1 - h) * Width + w;
int srcPos = h * Width + w;
raw[pos * 4 + 0] = Red[srcPos];
raw[pos * 4 + 1] = Green[srcPos];
raw[pos * 4 + 2] = Blue[srcPos];
raw[pos * 4 + 3] = Alpha[srcPos];
}
}
}
else
{
// Alpha only
for (int h = 0; h < Height; h++)
{
for (int w = 0; w < Width; w++)
{
int pos = (Height - 1 - h) * Width + w;
int srcPos = h * Width + w;
raw[pos * 4 + 0] = Alpha[srcPos];
raw[pos * 4 + 1] = Alpha[srcPos];
raw[pos * 4 + 2] = Alpha[srcPos];
raw[pos * 4 + 3] = Byte.MaxValue;
}
}
}
}
else
{
// RGB
for (int h = 0; h < Height; h++)
{
for (int w = 0; w < Width; w++)
{
int pos = (Height - 1 - h) * Width + w;
int srcPos = h * Width + w;
raw[pos * 4 + 0] = Red[srcPos];
raw[pos * 4 + 1] = Green[srcPos];
raw[pos * 4 + 2] = Blue[srcPos];
raw[pos * 4 + 3] = Byte.MaxValue;
}
}
}
return raw;
}
/// <summary>
/// Create a byte array containing 32-bit RGBA data with a bottom-left
/// origin, suitable for feeding directly into OpenGL
/// </summary>
/// <returns>A byte array containing raw texture data</returns>
public System.Drawing.Bitmap ExportBitmap()
{
byte[] raw = new byte[Width * Height * 4];
if ((Channels & ImageChannels.Alpha) != 0)
{
if ((Channels & ImageChannels.Color) != 0)
{
// RGBA
for (int pos = 0; pos < Height * Width; pos++)
{
raw[pos * 4 + 0] = Blue[pos];
raw[pos * 4 + 1] = Green[pos];
raw[pos * 4 + 2] = Red[pos];
raw[pos * 4 + 3] = Alpha[pos];
}
}
else
{
// Alpha only
for (int pos = 0; pos < Height * Width; pos++)
{
raw[pos * 4 + 0] = Alpha[pos];
raw[pos * 4 + 1] = Alpha[pos];
raw[pos * 4 + 2] = Alpha[pos];
raw[pos * 4 + 3] = Byte.MaxValue;
}
}
}
else
{
// RGB
for (int pos = 0; pos < Height * Width; pos++)
{
raw[pos * 4 + 0] = Blue[pos];
raw[pos * 4 + 1] = Green[pos];
raw[pos * 4 + 2] = Red[pos];
raw[pos * 4 + 3] = Byte.MaxValue;
}
}
System.Drawing.Bitmap b = new System.Drawing.Bitmap(
Width,
Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Drawing.Imaging.BitmapData bd = b.LockBits(new System.Drawing.Rectangle(0, 0, b.Width, b.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Runtime.InteropServices.Marshal.Copy(raw, 0, bd.Scan0, Width * Height * 4);
b.UnlockBits(bd);
return b;
}
public byte[] ExportTGA()
{
byte[] tga = new byte[Width * Height * ((Channels & ImageChannels.Alpha) == 0 ? 3 : 4) + 32];
int di = 0;
tga[di++] = 0; // idlength
tga[di++] = 0; // colormaptype = 0: no colormap
tga[di++] = 2; // image type = 2: uncompressed RGB
tga[di++] = 0; // color map spec is five zeroes for no color map
tga[di++] = 0; // color map spec is five zeroes for no color map
tga[di++] = 0; // color map spec is five zeroes for no color map
tga[di++] = 0; // color map spec is five zeroes for no color map
tga[di++] = 0; // color map spec is five zeroes for no color map
tga[di++] = 0; // x origin = two bytes
tga[di++] = 0; // x origin = two bytes
tga[di++] = 0; // y origin = two bytes
tga[di++] = 0; // y origin = two bytes
tga[di++] = (byte)(Width & 0xFF); // width - low byte
tga[di++] = (byte)(Width >> 8); // width - hi byte
tga[di++] = (byte)(Height & 0xFF); // height - low byte
tga[di++] = (byte)(Height >> 8); // height - hi byte
tga[di++] = (byte)((Channels & ImageChannels.Alpha) == 0 ? 24 : 32); // 24/32 bits per pixel
tga[di++] = (byte)((Channels & ImageChannels.Alpha) == 0 ? 32 : 40); // image descriptor byte
int n = Width * Height;
if ((Channels & ImageChannels.Alpha) != 0)
{
if ((Channels & ImageChannels.Color) != 0)
{
// RGBA
for (int i = 0; i < n; i++)
{
tga[di++] = Blue[i];
tga[di++] = Green[i];
tga[di++] = Red[i];
tga[di++] = Alpha[i];
}
}
else
{
// Alpha only
for (int i = 0; i < n; i++)
{
tga[di++] = Alpha[i];
tga[di++] = Alpha[i];
tga[di++] = Alpha[i];
tga[di++] = Byte.MaxValue;
}
}
}
else
{
// RGB
for (int i = 0; i < n; i++)
{
tga[di++] = Blue[i];
tga[di++] = Green[i];
tga[di++] = Red[i];
}
}
return tga;
}
private static void FillArray(byte[] array, byte value)
{
if (array != null)
{
for (int i = 0; i < array.Length; i++)
array[i] = value;
}
}
public void Clear()
{
FillArray(Red, 0);
FillArray(Green, 0);
FillArray(Blue, 0);
FillArray(Alpha, 0);
FillArray(Bump, 0);
}
public ManagedImage Clone()
{
ManagedImage image = new ManagedImage(Width, Height, Channels);
if (Red != null) image.Red = (byte[])Red.Clone();
if (Green != null) image.Green = (byte[])Green.Clone();
if (Blue != null) image.Blue = (byte[])Blue.Clone();
if (Alpha != null) image.Alpha = (byte[])Alpha.Clone();
if (Bump != null) image.Bump = (byte[])Bump.Clone();
return image;
}
}
}
+590
View File
@@ -0,0 +1,590 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace OpenMetaverse.Imaging
{
#if !NO_UNSAFE
/// <summary>
/// A Wrapper around openjpeg to encode and decode images to and from byte arrays
/// </summary>
public class OpenJPEG
{
/// <summary>TGA Header size</summary>
public const int TGA_HEADER_SIZE = 32;
#region JPEG2000 Structs
/// <summary>
/// Defines the beginning and ending file positions of a layer in an
/// LRCP-progression JPEG2000 file
/// </summary>
[System.Diagnostics.DebuggerDisplay("Start = {Start} End = {End} Size = {End - Start}")]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct J2KLayerInfo
{
public int Start;
public int End;
}
/// <summary>
/// This structure is used to marshal both encoded and decoded images.
/// MUST MATCH THE STRUCT IN dotnet.h!
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct MarshalledImage
{
public IntPtr encoded; // encoded image data
public int length; // encoded image length
public int dummy; // padding for 64-bit alignment
public IntPtr decoded; // decoded image, contiguous components
public int width; // width of decoded image
public int height; // height of decoded image
public int layers; // layer count
public int resolutions; // resolution count
public int components; // component count
public int packet_count; // packet count
public IntPtr packets; // pointer to the packets array
}
/// <summary>
/// Information about a single packet in a JPEG2000 stream
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct MarshalledPacket
{
/// <summary>Packet start position</summary>
public int start_pos;
/// <summary>Packet header end position</summary>
public int end_ph_pos;
/// <summary>Packet end position</summary>
public int end_pos;
public override string ToString()
{
return String.Format("start_pos: {0} end_ph_pos: {1} end_pos: {2}",
start_pos, end_ph_pos, end_pos);
}
}
#endregion JPEG2000 Structs
#region Unmanaged Function Declarations
// allocate encoded buffer based on length field
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetAllocEncoded(ref MarshalledImage image);
// allocate decoded buffer based on width and height fields
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetAllocDecoded(ref MarshalledImage image);
// free buffers
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetFree(ref MarshalledImage image);
// encode raw to jpeg2000
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetEncode(ref MarshalledImage image, bool lossless);
// decode jpeg2000 to raw
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetDecode(ref MarshalledImage image);
// decode jpeg2000 to raw, get jpeg2000 file info
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetDecodeWithInfo(ref MarshalledImage image);
// invoke 64 bit openjpeg calls
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetAllocEncoded64(ref MarshalledImage image);
// allocate decoded buffer based on width and height fields
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetAllocDecoded64(ref MarshalledImage image);
// free buffers
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetFree64(ref MarshalledImage image);
// encode raw to jpeg2000
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetEncode64(ref MarshalledImage image, bool lossless);
// decode jpeg2000 to raw
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetDecode64(ref MarshalledImage image);
// decode jpeg2000 to raw, get jpeg2000 file info
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool DotNetDecodeWithInfo64(ref MarshalledImage image);
#endregion Unmanaged Function Declarations
/// <summary>OpenJPEG is not threadsafe, so this object is used to lock
/// during calls into unmanaged code</summary>
private static object OpenJPEGLock = new object();
/// <summary>
/// Encode a <seealso cref="ManagedImage"/> object into a byte array
/// </summary>
/// <param name="image">The <seealso cref="ManagedImage"/> object to encode</param>
/// <param name="lossless">true to enable lossless conversion, only useful for small images ie: sculptmaps</param>
/// <returns>A byte array containing the encoded Image object</returns>
public static byte[] Encode(ManagedImage image, bool lossless)
{
if ((image.Channels & ManagedImage.ImageChannels.Color) == 0 ||
((image.Channels & ManagedImage.ImageChannels.Bump) != 0 && (image.Channels & ManagedImage.ImageChannels.Alpha) == 0))
throw new ArgumentException("JPEG2000 encoding is not supported for this channel combination");
byte[] encoded = null;
MarshalledImage marshalled = new MarshalledImage();
// allocate and copy to input buffer
marshalled.width = image.Width;
marshalled.height = image.Height;
marshalled.components = 3;
if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) marshalled.components++;
if ((image.Channels & ManagedImage.ImageChannels.Bump) != 0) marshalled.components++;
lock (OpenJPEGLock)
{
bool allocSuccess = (IntPtr.Size == 8) ? DotNetAllocDecoded64(ref marshalled) : DotNetAllocDecoded(ref marshalled);
if (!allocSuccess)
throw new Exception("DotNetAllocDecoded failed");
int n = image.Width * image.Height;
if ((image.Channels & ManagedImage.ImageChannels.Color) != 0)
{
Marshal.Copy(image.Red, 0, marshalled.decoded, n);
Marshal.Copy(image.Green, 0, (IntPtr)(marshalled.decoded.ToInt64() + n), n);
Marshal.Copy(image.Blue, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 2), n);
}
if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) Marshal.Copy(image.Alpha, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 3), n);
if ((image.Channels & ManagedImage.ImageChannels.Bump) != 0) Marshal.Copy(image.Bump, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 4), n);
// codec will allocate output buffer
bool encodeSuccess = (IntPtr.Size == 8) ? DotNetEncode64(ref marshalled, lossless) : DotNetEncode(ref marshalled, lossless);
if (!encodeSuccess)
throw new Exception("DotNetEncode failed");
// copy output buffer
encoded = new byte[marshalled.length];
Marshal.Copy(marshalled.encoded, encoded, 0, marshalled.length);
// free buffers
if (IntPtr.Size == 8)
DotNetFree64(ref marshalled);
else
DotNetFree(ref marshalled);
}
return encoded;
}
/// <summary>
/// Encode a <seealso cref="ManagedImage"/> object into a byte array
/// </summary>
/// <param name="image">The <seealso cref="ManagedImage"/> object to encode</param>
/// <returns>a byte array of the encoded image</returns>
public static byte[] Encode(ManagedImage image)
{
return Encode(image, false);
}
/// <summary>
/// Decode JPEG2000 data to an <seealso cref="System.Drawing.Image"/> and
/// <seealso cref="ManagedImage"/>
/// </summary>
/// <param name="encoded">JPEG2000 encoded data</param>
/// <param name="managedImage">ManagedImage object to decode to</param>
/// <param name="image">Image object to decode to</param>
/// <returns>True if the decode succeeds, otherwise false</returns>
public static bool DecodeToImage(byte[] encoded, out ManagedImage managedImage, out Image image)
{
managedImage = null;
image = null;
if (DecodeToImage(encoded, out managedImage))
{
try
{
image = managedImage.ExportBitmap();
return true;
}
catch (Exception ex)
{
Logger.Log("Failed to export and load TGA data from decoded image", Helpers.LogLevel.Error, ex);
return false;
}
}
else
{
return false;
}
}
/// <summary>
///
/// </summary>
/// <param name="encoded"></param>
/// <param name="managedImage"></param>
/// <returns></returns>
public static bool DecodeToImage(byte[] encoded, out ManagedImage managedImage)
{
MarshalledImage marshalled = new MarshalledImage();
// Allocate and copy to input buffer
marshalled.length = encoded.Length;
lock (OpenJPEGLock)
{
if (IntPtr.Size == 8)
DotNetAllocEncoded64(ref marshalled);
else
DotNetAllocEncoded(ref marshalled);
Marshal.Copy(encoded, 0, marshalled.encoded, encoded.Length);
// Codec will allocate output buffer
if (IntPtr.Size == 8)
DotNetDecode64(ref marshalled);
else
DotNetDecode(ref marshalled);
int n = marshalled.width * marshalled.height;
switch (marshalled.components)
{
case 1: // Grayscale
managedImage = new ManagedImage(marshalled.width, marshalled.height,
ManagedImage.ImageChannels.Color);
Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
Buffer.BlockCopy(managedImage.Red, 0, managedImage.Green, 0, n);
Buffer.BlockCopy(managedImage.Red, 0, managedImage.Blue, 0, n);
break;
case 2: // Grayscale + alpha
managedImage = new ManagedImage(marshalled.width, marshalled.height,
ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
Buffer.BlockCopy(managedImage.Red, 0, managedImage.Green, 0, n);
Buffer.BlockCopy(managedImage.Red, 0, managedImage.Blue, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Alpha, 0, n);
break;
case 3: // RGB
managedImage = new ManagedImage(marshalled.width, marshalled.height,
ManagedImage.ImageChannels.Color);
Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n);
break;
case 4: // RGBA
managedImage = new ManagedImage(marshalled.width, marshalled.height,
ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 3)), managedImage.Alpha, 0, n);
break;
case 5: // RGBAB
managedImage = new ManagedImage(marshalled.width, marshalled.height,
ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump);
Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 3)), managedImage.Alpha, 0, n);
Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 4)), managedImage.Bump, 0, n);
break;
default:
Logger.Log("Decoded image with unhandled number of components: " + marshalled.components,
Helpers.LogLevel.Error);
if (IntPtr.Size == 8)
DotNetFree64(ref marshalled);
else
DotNetFree(ref marshalled);
managedImage = null;
return false;
}
if (IntPtr.Size == 8)
DotNetFree64(ref marshalled);
else
DotNetFree(ref marshalled);
}
return true;
}
/// <summary>
///
/// </summary>
/// <param name="encoded"></param>
/// <param name="layerInfo"></param>
/// <param name="components"></param>
/// <returns></returns>
public static bool DecodeLayerBoundaries(byte[] encoded, out J2KLayerInfo[] layerInfo, out int components)
{
bool success = false;
layerInfo = null;
components = 0;
MarshalledImage marshalled = new MarshalledImage();
// Allocate and copy to input buffer
marshalled.length = encoded.Length;
lock (OpenJPEGLock)
{
if (IntPtr.Size == 8)
DotNetAllocEncoded64(ref marshalled);
else
DotNetAllocEncoded(ref marshalled);
Marshal.Copy(encoded, 0, marshalled.encoded, encoded.Length);
// Run the decode
bool decodeSuccess = (IntPtr.Size == 8) ? DotNetDecodeWithInfo64(ref marshalled) : DotNetDecodeWithInfo(ref marshalled);
if (decodeSuccess)
{
components = marshalled.components;
// Sanity check
if (marshalled.layers * marshalled.resolutions * marshalled.components == marshalled.packet_count)
{
// Manually marshal the array of opj_packet_info structs
MarshalledPacket[] packets = new MarshalledPacket[marshalled.packet_count];
int offset = 0;
for (int i = 0; i < marshalled.packet_count; i++)
{
MarshalledPacket packet;
packet.start_pos = Marshal.ReadInt32(marshalled.packets, offset);
offset += 4;
packet.end_ph_pos = Marshal.ReadInt32(marshalled.packets, offset);
offset += 4;
packet.end_pos = Marshal.ReadInt32(marshalled.packets, offset);
offset += 4;
//double distortion = (double)Marshal.ReadInt64(marshalled.packets, offset);
offset += 8;
packets[i] = packet;
}
layerInfo = new J2KLayerInfo[marshalled.layers];
for (int i = 0; i < marshalled.layers; i++)
{
int packetsPerLayer = marshalled.packet_count / marshalled.layers;
MarshalledPacket startPacket = packets[packetsPerLayer * i];
MarshalledPacket endPacket = packets[(packetsPerLayer * (i + 1)) - 1];
layerInfo[i].Start = startPacket.start_pos;
layerInfo[i].End = endPacket.end_pos;
}
// More sanity checking
if (layerInfo.Length == 0 || layerInfo[layerInfo.Length - 1].End <= encoded.Length - 1)
{
success = true;
for (int i = 0; i < layerInfo.Length; i++)
{
if (layerInfo[i].Start >= layerInfo[i].End ||
(i > 0 && layerInfo[i].Start <= layerInfo[i - 1].End))
{
System.Text.StringBuilder output = new System.Text.StringBuilder(
"Inconsistent packet data in JPEG2000 stream:\n");
for (int j = 0; j < layerInfo.Length; j++)
output.AppendFormat("Layer {0}: Start: {1} End: {2}\n", j, layerInfo[j].Start, layerInfo[j].End);
Logger.DebugLog(output.ToString());
success = false;
break;
}
}
if (!success)
{
for (int i = 0; i < layerInfo.Length; i++)
{
if (i < layerInfo.Length - 1)
layerInfo[i].End = layerInfo[i + 1].Start - 1;
else
layerInfo[i].End = marshalled.length;
}
Logger.DebugLog("Corrected JPEG2000 packet data");
success = true;
for (int i = 0; i < layerInfo.Length; i++)
{
if (layerInfo[i].Start >= layerInfo[i].End ||
(i > 0 && layerInfo[i].Start <= layerInfo[i - 1].End))
{
System.Text.StringBuilder output = new System.Text.StringBuilder(
"Still inconsistent packet data in JPEG2000 stream, giving up:\n");
for (int j = 0; j < layerInfo.Length; j++)
output.AppendFormat("Layer {0}: Start: {1} End: {2}\n", j, layerInfo[j].Start, layerInfo[j].End);
Logger.DebugLog(output.ToString());
success = false;
break;
}
}
}
}
else
{
Logger.Log(String.Format(
"Last packet end in JPEG2000 stream extends beyond the end of the file. filesize={0} layerend={1}",
encoded.Length, layerInfo[layerInfo.Length - 1].End), Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log(String.Format(
"Packet count mismatch in JPEG2000 stream. layers={0} resolutions={1} components={2} packets={3}",
marshalled.layers, marshalled.resolutions, marshalled.components, marshalled.packet_count),
Helpers.LogLevel.Warning);
}
}
if (IntPtr.Size == 8)
DotNetFree64(ref marshalled);
else
DotNetFree(ref marshalled);
}
return success;
}
/// <summary>
/// Encode a <seealso cref="System.Drawing.Bitmap"/> object into a byte array
/// </summary>
/// <param name="bitmap">The source <seealso cref="System.Drawing.Bitmap"/> object to encode</param>
/// <param name="lossless">true to enable lossless decoding</param>
/// <returns>A byte array containing the source Bitmap object</returns>
public unsafe static byte[] EncodeFromImage(Bitmap bitmap, bool lossless)
{
BitmapData bd;
ManagedImage decoded;
int bitmapWidth = bitmap.Width;
int bitmapHeight = bitmap.Height;
int pixelCount = bitmapWidth * bitmapHeight;
int i;
if ((bitmap.PixelFormat & PixelFormat.Alpha) != 0 || (bitmap.PixelFormat & PixelFormat.PAlpha) != 0)
{
// Four layers, RGBA
decoded = new ManagedImage(bitmapWidth, bitmapHeight,
ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* pixel = (byte*)bd.Scan0;
for (i = 0; i < pixelCount; i++)
{
// GDI+ gives us BGRA and we need to turn that in to RGBA
decoded.Blue[i] = *(pixel++);
decoded.Green[i] = *(pixel++);
decoded.Red[i] = *(pixel++);
decoded.Alpha[i] = *(pixel++);
}
}
else if (bitmap.PixelFormat == PixelFormat.Format16bppGrayScale)
{
// One layer
decoded = new ManagedImage(bitmapWidth, bitmapHeight,
ManagedImage.ImageChannels.Color);
bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight),
ImageLockMode.ReadOnly, PixelFormat.Format16bppGrayScale);
byte* pixel = (byte*)bd.Scan0;
for (i = 0; i < pixelCount; i++)
{
// Normalize 16-bit data down to 8-bit
ushort origVal = (byte)(*(pixel) + (*(pixel + 1) << 8));
byte val = (byte)(((double)origVal / (double)UInt32.MaxValue) * (double)Byte.MaxValue);
decoded.Red[i] = val;
decoded.Green[i] = val;
decoded.Blue[i] = val;
pixel += 2;
}
}
else
{
// Three layers, RGB
decoded = new ManagedImage(bitmapWidth, bitmapHeight,
ManagedImage.ImageChannels.Color);
bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte* pixel = (byte*)bd.Scan0;
for (i = 0; i < pixelCount; i++)
{
decoded.Blue[i] = *(pixel++);
decoded.Green[i] = *(pixel++);
decoded.Red[i] = *(pixel++);
}
}
bitmap.UnlockBits(bd);
byte[] encoded = Encode(decoded, lossless);
return encoded;
}
}
#endif
}
+640
View File
@@ -0,0 +1,640 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse.Imaging
{
#if !NO_UNSAFE
/// <summary>
/// Capability to load TGAs to Bitmap
/// </summary>
public class LoadTGAClass
{
struct tgaColorMap
{
public ushort FirstEntryIndex;
public ushort Length;
public byte EntrySize;
public void Read(System.IO.BinaryReader br)
{
FirstEntryIndex = br.ReadUInt16();
Length = br.ReadUInt16();
EntrySize = br.ReadByte();
}
}
struct tgaImageSpec
{
public ushort XOrigin;
public ushort YOrigin;
public ushort Width;
public ushort Height;
public byte PixelDepth;
public byte Descriptor;
public void Read(System.IO.BinaryReader br)
{
XOrigin = br.ReadUInt16();
YOrigin = br.ReadUInt16();
Width = br.ReadUInt16();
Height = br.ReadUInt16();
PixelDepth = br.ReadByte();
Descriptor = br.ReadByte();
}
public byte AlphaBits
{
get
{
return (byte)(Descriptor & 0xF);
}
set
{
Descriptor = (byte)((Descriptor & ~0xF) | (value & 0xF));
}
}
public bool BottomUp
{
get
{
return (Descriptor & 0x20) == 0x20;
}
set
{
Descriptor = (byte)((Descriptor & ~0x20) | (value ? 0x20 : 0));
}
}
}
struct tgaHeader
{
public byte IdLength;
public byte ColorMapType;
public byte ImageType;
public tgaColorMap ColorMap;
public tgaImageSpec ImageSpec;
public void Read(System.IO.BinaryReader br)
{
this.IdLength = br.ReadByte();
this.ColorMapType = br.ReadByte();
this.ImageType = br.ReadByte();
this.ColorMap = new tgaColorMap();
this.ImageSpec = new tgaImageSpec();
this.ColorMap.Read(br);
this.ImageSpec.Read(br);
}
public bool RleEncoded
{
get
{
return ImageType >= 9;
}
}
}
struct tgaCD
{
public uint RMask, GMask, BMask, AMask;
public byte RShift, GShift, BShift, AShift;
public uint FinalOr;
public bool NeedNoConvert;
}
static uint UnpackColor(
uint sourceColor, ref tgaCD cd)
{
if (cd.RMask == 0xFF && cd.GMask == 0xFF && cd.BMask == 0xFF)
{
// Special case to deal with 8-bit TGA files that we treat as alpha masks
return sourceColor << 24;
}
else
{
uint rpermute = (sourceColor << cd.RShift) | (sourceColor >> (32 - cd.RShift));
uint gpermute = (sourceColor << cd.GShift) | (sourceColor >> (32 - cd.GShift));
uint bpermute = (sourceColor << cd.BShift) | (sourceColor >> (32 - cd.BShift));
uint apermute = (sourceColor << cd.AShift) | (sourceColor >> (32 - cd.AShift));
uint result =
(rpermute & cd.RMask) | (gpermute & cd.GMask)
| (bpermute & cd.BMask) | (apermute & cd.AMask) | cd.FinalOr;
return result;
}
}
static unsafe void decodeLine(
System.Drawing.Imaging.BitmapData b,
int line,
int byp,
byte[] data,
ref tgaCD cd)
{
if (cd.NeedNoConvert)
{
// fast copy
uint* linep = (uint*)((byte*)b.Scan0.ToPointer() + line * b.Stride);
fixed (byte* ptr = data)
{
uint* sptr = (uint*)ptr;
for (int i = 0; i < b.Width; ++i)
{
linep[i] = sptr[i];
}
}
}
else
{
byte* linep = (byte*)b.Scan0.ToPointer() + line * b.Stride;
uint* up = (uint*)linep;
int rdi = 0;
fixed (byte* ptr = data)
{
for (int i = 0; i < b.Width; ++i)
{
uint x = 0;
for (int j = 0; j < byp; ++j)
{
x |= ((uint)ptr[rdi]) << (j << 3);
++rdi;
}
up[i] = UnpackColor(x, ref cd);
}
}
}
}
static void decodeRle(
System.Drawing.Imaging.BitmapData b,
int byp, tgaCD cd, System.IO.BinaryReader br, bool bottomUp)
{
try
{
int w = b.Width;
// make buffer larger, so in case of emergency I can decode
// over line ends.
byte[] linebuffer = new byte[(w + 128) * byp];
int maxindex = w * byp;
int index = 0;
for (int j = 0; j < b.Height; ++j)
{
while (index < maxindex)
{
byte blocktype = br.ReadByte();
int bytestoread;
int bytestocopy;
if (blocktype >= 0x80)
{
bytestoread = byp;
bytestocopy = byp * (blocktype - 0x80);
}
else
{
bytestoread = byp * (blocktype + 1);
bytestocopy = 0;
}
//if (index + bytestoread > maxindex)
// throw new System.ArgumentException ("Corrupt TGA");
br.Read(linebuffer, index, bytestoread);
index += bytestoread;
for (int i = 0; i != bytestocopy; ++i)
{
linebuffer[index + i] = linebuffer[index + i - bytestoread];
}
index += bytestocopy;
}
if (!bottomUp)
decodeLine(b, b.Height - j - 1, byp, linebuffer, ref cd);
else
decodeLine(b, j, byp, linebuffer, ref cd);
if (index > maxindex)
{
Array.Copy(linebuffer, maxindex, linebuffer, 0, index - maxindex);
index -= maxindex;
}
else
index = 0;
}
}
catch (System.IO.EndOfStreamException)
{
}
}
static void decodePlain(
System.Drawing.Imaging.BitmapData b,
int byp, tgaCD cd, System.IO.BinaryReader br, bool bottomUp)
{
int w = b.Width;
byte[] linebuffer = new byte[w * byp];
for (int j = 0; j < b.Height; ++j)
{
br.Read(linebuffer, 0, w * byp);
if (!bottomUp)
decodeLine(b, b.Height - j - 1, byp, linebuffer, ref cd);
else
decodeLine(b, j, byp, linebuffer, ref cd);
}
}
static void decodeStandard8(
System.Drawing.Imaging.BitmapData b,
tgaHeader hdr,
System.IO.BinaryReader br)
{
tgaCD cd = new tgaCD();
cd.RMask = 0x000000ff;
cd.GMask = 0x000000ff;
cd.BMask = 0x000000ff;
cd.AMask = 0x000000ff;
cd.RShift = 0;
cd.GShift = 0;
cd.BShift = 0;
cd.AShift = 0;
cd.FinalOr = 0x00000000;
if (hdr.RleEncoded)
decodeRle(b, 1, cd, br, hdr.ImageSpec.BottomUp);
else
decodePlain(b, 1, cd, br, hdr.ImageSpec.BottomUp);
}
static void decodeSpecial16(
System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br)
{
// i must convert the input stream to a sequence of uint values
// which I then unpack.
tgaCD cd = new tgaCD();
cd.RMask = 0x00f00000;
cd.GMask = 0x0000f000;
cd.BMask = 0x000000f0;
cd.AMask = 0xf0000000;
cd.RShift = 12;
cd.GShift = 8;
cd.BShift = 4;
cd.AShift = 16;
cd.FinalOr = 0;
if (hdr.RleEncoded)
decodeRle(b, 2, cd, br, hdr.ImageSpec.BottomUp);
else
decodePlain(b, 2, cd, br, hdr.ImageSpec.BottomUp);
}
static void decodeStandard16(
System.Drawing.Imaging.BitmapData b,
tgaHeader hdr,
System.IO.BinaryReader br)
{
// i must convert the input stream to a sequence of uint values
// which I then unpack.
tgaCD cd = new tgaCD();
cd.RMask = 0x00f80000; // from 0xF800
cd.GMask = 0x0000fc00; // from 0x07E0
cd.BMask = 0x000000f8; // from 0x001F
cd.AMask = 0x00000000;
cd.RShift = 8;
cd.GShift = 5;
cd.BShift = 3;
cd.AShift = 0;
cd.FinalOr = 0xff000000;
if (hdr.RleEncoded)
decodeRle(b, 2, cd, br, hdr.ImageSpec.BottomUp);
else
decodePlain(b, 2, cd, br, hdr.ImageSpec.BottomUp);
}
static void decodeSpecial24(System.Drawing.Imaging.BitmapData b,
tgaHeader hdr, System.IO.BinaryReader br)
{
// i must convert the input stream to a sequence of uint values
// which I then unpack.
tgaCD cd = new tgaCD();
cd.RMask = 0x00f80000;
cd.GMask = 0x0000fc00;
cd.BMask = 0x000000f8;
cd.AMask = 0xff000000;
cd.RShift = 8;
cd.GShift = 5;
cd.BShift = 3;
cd.AShift = 8;
cd.FinalOr = 0;
if (hdr.RleEncoded)
decodeRle(b, 3, cd, br, hdr.ImageSpec.BottomUp);
else
decodePlain(b, 3, cd, br, hdr.ImageSpec.BottomUp);
}
static void decodeStandard24(System.Drawing.Imaging.BitmapData b,
tgaHeader hdr, System.IO.BinaryReader br)
{
// i must convert the input stream to a sequence of uint values
// which I then unpack.
tgaCD cd = new tgaCD();
cd.RMask = 0x00ff0000;
cd.GMask = 0x0000ff00;
cd.BMask = 0x000000ff;
cd.AMask = 0x00000000;
cd.RShift = 0;
cd.GShift = 0;
cd.BShift = 0;
cd.AShift = 0;
cd.FinalOr = 0xff000000;
if (hdr.RleEncoded)
decodeRle(b, 3, cd, br, hdr.ImageSpec.BottomUp);
else
decodePlain(b, 3, cd, br, hdr.ImageSpec.BottomUp);
}
static void decodeStandard32(System.Drawing.Imaging.BitmapData b,
tgaHeader hdr, System.IO.BinaryReader br)
{
// i must convert the input stream to a sequence of uint values
// which I then unpack.
tgaCD cd = new tgaCD();
cd.RMask = 0x00ff0000;
cd.GMask = 0x0000ff00;
cd.BMask = 0x000000ff;
cd.AMask = 0xff000000;
cd.RShift = 0;
cd.GShift = 0;
cd.BShift = 0;
cd.AShift = 0;
cd.FinalOr = 0x00000000;
cd.NeedNoConvert = true;
if (hdr.RleEncoded)
decodeRle(b, 4, cd, br, hdr.ImageSpec.BottomUp);
else
decodePlain(b, 4, cd, br, hdr.ImageSpec.BottomUp);
}
public static System.Drawing.Size GetTGASize(string filename)
{
System.IO.FileStream f = System.IO.File.OpenRead(filename);
System.IO.BinaryReader br = new System.IO.BinaryReader(f);
tgaHeader header = new tgaHeader();
header.Read(br);
br.Close();
return new System.Drawing.Size(header.ImageSpec.Width, header.ImageSpec.Height);
}
public static System.Drawing.Bitmap LoadTGA(System.IO.Stream source)
{
byte[] buffer = new byte[source.Length];
source.Read(buffer, 0, buffer.Length);
System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer);
using (System.IO.BinaryReader br = new System.IO.BinaryReader(ms))
{
tgaHeader header = new tgaHeader();
header.Read(br);
if (header.ImageSpec.PixelDepth != 8 &&
header.ImageSpec.PixelDepth != 16 &&
header.ImageSpec.PixelDepth != 24 &&
header.ImageSpec.PixelDepth != 32)
throw new ArgumentException("Not a supported tga file.");
if (header.ImageSpec.AlphaBits > 8)
throw new ArgumentException("Not a supported tga file.");
if (header.ImageSpec.Width > 4096 ||
header.ImageSpec.Height > 4096)
throw new ArgumentException("Image too large.");
System.Drawing.Bitmap b;
System.Drawing.Imaging.BitmapData bd;
// Create a bitmap for the image.
// Only include an alpha layer when the image requires one.
if (header.ImageSpec.AlphaBits > 0 ||
header.ImageSpec.PixelDepth == 8 || // Assume 8 bit images are alpha only
header.ImageSpec.PixelDepth == 32) // Assume 32 bit images are ARGB
{ // Image needs an alpha layer
b = new System.Drawing.Bitmap(
header.ImageSpec.Width,
header.ImageSpec.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
bd = b.LockBits(new System.Drawing.Rectangle(0, 0, b.Width, b.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
}
else
{ // Image does not need an alpha layer, so do not include one.
b = new System.Drawing.Bitmap(
header.ImageSpec.Width,
header.ImageSpec.Height,
System.Drawing.Imaging.PixelFormat.Format32bppRgb);
bd = b.LockBits(new System.Drawing.Rectangle(0, 0, b.Width, b.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppRgb);
}
switch (header.ImageSpec.PixelDepth)
{
case 8:
decodeStandard8(bd, header, br);
break;
case 16:
if (header.ImageSpec.AlphaBits > 0)
decodeSpecial16(bd, header, br);
else
decodeStandard16(bd, header, br);
break;
case 24:
if (header.ImageSpec.AlphaBits > 0)
decodeSpecial24(bd, header, br);
else
decodeStandard24(bd, header, br);
break;
case 32:
decodeStandard32(bd, header, br);
break;
default:
b.UnlockBits(bd);
b.Dispose();
return null;
}
b.UnlockBits(bd);
return b;
}
}
public static unsafe ManagedImage LoadTGAImage(System.IO.Stream source)
{
return LoadTGAImage(source, false);
}
public static unsafe ManagedImage LoadTGAImage(System.IO.Stream source, bool mask)
{
byte[] buffer = new byte[source.Length];
source.Read(buffer, 0, buffer.Length);
System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer);
using (System.IO.BinaryReader br = new System.IO.BinaryReader(ms))
{
tgaHeader header = new tgaHeader();
header.Read(br);
if (header.ImageSpec.PixelDepth != 8 &&
header.ImageSpec.PixelDepth != 16 &&
header.ImageSpec.PixelDepth != 24 &&
header.ImageSpec.PixelDepth != 32)
throw new ArgumentException("Not a supported tga file.");
if (header.ImageSpec.AlphaBits > 8)
throw new ArgumentException("Not a supported tga file.");
if (header.ImageSpec.Width > 4096 ||
header.ImageSpec.Height > 4096)
throw new ArgumentException("Image too large.");
byte[] decoded = new byte[header.ImageSpec.Width * header.ImageSpec.Height * 4];
System.Drawing.Imaging.BitmapData bd = new System.Drawing.Imaging.BitmapData();
fixed (byte* pdecoded = &decoded[0])
{
bd.Width = header.ImageSpec.Width;
bd.Height = header.ImageSpec.Height;
bd.PixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppPArgb;
bd.Stride = header.ImageSpec.Width * 4;
bd.Scan0 = (IntPtr)pdecoded;
switch (header.ImageSpec.PixelDepth)
{
case 8:
decodeStandard8(bd, header, br);
break;
case 16:
if (header.ImageSpec.AlphaBits > 0)
decodeSpecial16(bd, header, br);
else
decodeStandard16(bd, header, br);
break;
case 24:
if (header.ImageSpec.AlphaBits > 0)
decodeSpecial24(bd, header, br);
else
decodeStandard24(bd, header, br);
break;
case 32:
decodeStandard32(bd, header, br);
break;
default:
return null;
}
}
int n = header.ImageSpec.Width * header.ImageSpec.Height;
ManagedImage image;
if (mask && header.ImageSpec.AlphaBits == 0 && header.ImageSpec.PixelDepth == 8)
{
image = new ManagedImage(header.ImageSpec.Width, header.ImageSpec.Height,
ManagedImage.ImageChannels.Alpha);
int p = 3;
for (int i = 0; i < n; i++)
{
image.Alpha[i] = decoded[p];
p += 4;
}
}
else
{
image = new ManagedImage(header.ImageSpec.Width, header.ImageSpec.Height,
ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
int p = 0;
for (int i = 0; i < n; i++)
{
image.Blue[i] = decoded[p++];
image.Green[i] = decoded[p++];
image.Red[i] = decoded[p++];
image.Alpha[i] = decoded[p++];
}
}
br.Close();
return image;
}
}
public static System.Drawing.Bitmap LoadTGA(string filename)
{
try
{
using (System.IO.FileStream f = System.IO.File.OpenRead(filename))
{
return LoadTGA(f);
}
}
catch (System.IO.DirectoryNotFoundException)
{
return null; // file not found
}
catch (System.IO.FileNotFoundException)
{
return null; // file not found
}
}
}
#endif
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,714 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text.RegularExpressions;
using System.IO;
using System.Xml;
using System.Drawing;
using System.Xml.Serialization;
using OpenMetaverse.ImportExport.Collada14;
using OpenMetaverse.Rendering;
using OpenMetaverse.Imaging;
namespace OpenMetaverse.ImportExport
{
/// <summary>
/// Parsing Collada model files into data structures
/// </summary>
public class ColladaLoader
{
COLLADA Model;
static XmlSerializer Serializer = null;
List<Node> Nodes;
List<ModelMaterial> Materials;
Dictionary<string, string> MatSymTarget;
string FileName;
class Node
{
public Matrix4 Transform = Matrix4.Identity;
public string Name;
public string ID;
public string MeshID;
}
/// <summary>
/// Parses Collada document
/// </summary>
/// <param name="filename">Load .dae model from this file</param>
/// <param name="loadImages">Load and decode images for uploading with model</param>
/// <returns>A list of mesh prims that were parsed from the collada file</returns>
public List<ModelPrim> Load(string filename, bool loadImages)
{
try
{
// Create an instance of the XmlSerializer specifying type and namespace.
if (Serializer == null)
{
Serializer = new XmlSerializer(typeof(COLLADA));
}
this.FileName = filename;
// A FileStream is needed to read the XML document.
FileStream fs = new FileStream(filename, FileMode.Open);
XmlReader reader = XmlReader.Create(fs);
Model = (COLLADA)Serializer.Deserialize(reader);
fs.Close();
var prims = Parse();
if (loadImages)
{
LoadImages(prims);
}
return prims;
}
catch (Exception ex)
{
Logger.Log("Failed parsing collada file: " + ex.Message, Helpers.LogLevel.Error, ex);
return new List<ModelPrim>();
}
}
void LoadImages(List<ModelPrim> prims)
{
foreach (var prim in prims)
{
foreach (var face in prim.Faces)
{
if (!string.IsNullOrEmpty(face.Material.Texture))
{
LoadImage(face.Material);
}
}
}
}
void LoadImage(ModelMaterial material)
{
var fname = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileName), material.Texture);
try
{
string ext = System.IO.Path.GetExtension(material.Texture).ToLower();
Bitmap bitmap = null;
if (ext == ".jp2" || ext == ".j2c")
{
material.TextureData = File.ReadAllBytes(fname);
return;
}
if (ext == ".tga")
{
bitmap = LoadTGAClass.LoadTGA(fname);
}
else
{
bitmap = (Bitmap)Image.FromFile(fname);
}
int width = bitmap.Width;
int height = bitmap.Height;
// Handle resizing to prevent excessively large images and irregular dimensions
if (!IsPowerOfTwo((uint)width) || !IsPowerOfTwo((uint)height) || width > 1024 || height > 1024)
{
var origWidth = width;
var origHieght = height;
width = ClosestPowerOwTwo(width);
height = ClosestPowerOwTwo(height);
width = width > 1024 ? 1024 : width;
height = height > 1024 ? 1024 : height;
Logger.Log("Image has irregular dimensions " + origWidth + "x" + origHieght + ". Resizing to " + width + "x" + height, Helpers.LogLevel.Info);
Bitmap resized = new Bitmap(width, height, bitmap.PixelFormat);
Graphics graphics = Graphics.FromImage(resized);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.DrawImage(bitmap, 0, 0, width, height);
bitmap.Dispose();
bitmap = resized;
}
material.TextureData = OpenJPEG.EncodeFromImage(bitmap, false);
Logger.Log("Successfully encoded " + fname, Helpers.LogLevel.Info);
}
catch (Exception ex)
{
Logger.Log("Failed loading " + fname + ": " + ex.Message, Helpers.LogLevel.Warning);
}
}
bool IsPowerOfTwo(uint n)
{
return (n & (n - 1)) == 0 && n != 0;
}
int ClosestPowerOwTwo(int n)
{
int res = 1;
while (res < n)
{
res <<= 1;
}
return res > 1 ? res / 2 : 1;
}
ModelMaterial ExtractMaterial(object diffuse)
{
ModelMaterial ret = new ModelMaterial();
if (diffuse is common_color_or_texture_typeColor)
{
var col = (common_color_or_texture_typeColor)diffuse;
ret.DiffuseColor = new Color4((float)col.Values[0], (float)col.Values[1], (float)col.Values[2], (float)col.Values[3]);
}
else if (diffuse is common_color_or_texture_typeTexture)
{
var tex = (common_color_or_texture_typeTexture)diffuse;
ret.Texture = tex.texcoord;
}
return ret;
}
void ParseMaterials()
{
if (Model == null) return;
Materials = new List<ModelMaterial>();
// Material -> effect mapping
Dictionary<string, string> matEffect = new Dictionary<string, string>();
List<ModelMaterial> tmpEffects = new List<ModelMaterial>();
// Image ID -> filename mapping
Dictionary<string, string> imgMap = new Dictionary<string, string>();
foreach (var item in Model.Items)
{
if (item is library_images)
{
var images = (library_images)item;
if (images.image != null)
{
foreach (var image in images.image)
{
var img = (image)image;
string ID = img.id;
if (img.Item is string)
{
imgMap[ID] = (string)img.Item;
}
}
}
}
}
foreach (var item in Model.Items)
{
if (item is library_materials)
{
var materials = (library_materials)item;
if (materials.material != null)
{
foreach (var material in materials.material)
{
var ID = material.id;
if (material.instance_effect != null)
{
if (!string.IsNullOrEmpty(material.instance_effect.url))
{
matEffect[material.instance_effect.url.Substring(1)] = ID;
}
}
}
}
}
}
foreach (var item in Model.Items)
{
if (item is library_effects)
{
var effects = (library_effects)item;
if (effects.effect != null)
{
foreach (var effect in effects.effect)
{
string ID = effect.id;
foreach (var effItem in effect.Items)
{
if (effItem is effectFx_profile_abstractProfile_COMMON)
{
var teq = ((effectFx_profile_abstractProfile_COMMON)effItem).technique;
if (teq != null)
{
if (teq.Item is effectFx_profile_abstractProfile_COMMONTechniquePhong)
{
var shader = (effectFx_profile_abstractProfile_COMMONTechniquePhong)teq.Item;
if (shader.diffuse != null)
{
var material = ExtractMaterial(shader.diffuse.Item);
material.ID = ID;
tmpEffects.Add(material);
}
}
else if (teq.Item is effectFx_profile_abstractProfile_COMMONTechniqueLambert)
{
var shader = (effectFx_profile_abstractProfile_COMMONTechniqueLambert)teq.Item;
if (shader.diffuse != null)
{
var material = ExtractMaterial(shader.diffuse.Item);
material.ID = ID;
tmpEffects.Add(material);
}
}
}
}
}
}
}
}
}
foreach (var effect in tmpEffects)
{
if (matEffect.ContainsKey(effect.ID))
{
effect.ID = matEffect[effect.ID];
if (!string.IsNullOrEmpty(effect.Texture))
{
if (imgMap.ContainsKey(effect.Texture))
{
effect.Texture = imgMap[effect.Texture];
}
}
Materials.Add(effect);
}
}
}
void ProcessNode(node node)
{
Node n = new Node();
n.ID = node.id;
if (node.Items != null)
// Try finding matrix
foreach (var i in node.Items)
{
if (i is matrix)
{
var m = (matrix)i;
for (int a = 0; a < 4; a++)
for (int b = 0; b < 4; b++)
{
n.Transform[b, a] = (float)m.Values[a * 4 + b];
}
}
}
// Find geometry and material
if (node.instance_geometry != null && node.instance_geometry.Length > 0)
{
var instGeom = node.instance_geometry[0];
if (!string.IsNullOrEmpty(instGeom.url))
{
n.MeshID = instGeom.url.Substring(1);
}
if (instGeom.bind_material != null && instGeom.bind_material.technique_common != null)
{
foreach (var teq in instGeom.bind_material.technique_common)
{
var target = teq.target;
if (!string.IsNullOrEmpty(target))
{
target = target.Substring(1);
MatSymTarget[teq.symbol] = target;
}
}
}
}
if (node.Items != null && node.instance_geometry != null && node.instance_geometry.Length > 0)
Nodes.Add(n);
// Recurse if the scene is hierarchical
if (node.node1 != null)
foreach (node nd in node.node1)
ProcessNode(nd);
}
void ParseVisualScene()
{
Nodes = new List<Node>();
if (Model == null) return;
MatSymTarget = new Dictionary<string, string>();
foreach (var item in Model.Items)
{
if (item is library_visual_scenes)
{
var scene = ((library_visual_scenes)item).visual_scene[0];
foreach (var node in scene.node)
{
ProcessNode(node);
}
}
}
}
List<ModelPrim> Parse()
{
var Prims = new List<ModelPrim>();
float DEG_TO_RAD = 0.017453292519943295769236907684886f;
if (Model == null) return Prims;
Matrix4 transform = Matrix4.Identity;
UpAxisType upAxis = UpAxisType.Y_UP;
var asset = Model.asset;
if (asset != null)
{
upAxis = asset.up_axis;
if (asset.unit != null)
{
float meter = (float)asset.unit.meter;
transform[0, 0] = meter;
transform[1, 1] = meter;
transform[2, 2] = meter;
}
}
Matrix4 rotation = Matrix4.Identity;
if (upAxis == UpAxisType.X_UP)
{
rotation = Matrix4.CreateFromEulers(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
}
else if (upAxis == UpAxisType.Y_UP)
{
rotation = Matrix4.CreateFromEulers(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
}
rotation = rotation * transform;
transform = rotation;
ParseVisualScene();
ParseMaterials();
foreach (var item in Model.Items)
{
if (item is library_geometries)
{
var geometries = (library_geometries)item;
foreach (var geo in geometries.geometry)
{
var mesh = geo.Item as mesh;
if (mesh == null) continue;
var nodes = Nodes.FindAll(n => n.MeshID == geo.id);
if (nodes != null)
{
byte[] mesh_asset = null;
foreach (var node in nodes)
{
var prim = new ModelPrim();
prim.ID = node.ID;
Prims.Add(prim);
Matrix4 primTransform = transform;
primTransform = primTransform * node.Transform;
AddPositions(mesh, prim, primTransform);
foreach (var mitem in mesh.Items)
{
if (mitem is triangles)
{
AddFacesFromPolyList(Triangles2Polylist((triangles)mitem), mesh, prim, primTransform);
}
if (mitem is polylist)
{
AddFacesFromPolyList((polylist)mitem, mesh, prim, primTransform);
}
}
if (mesh_asset == null)
{
prim.CreateAsset(UUID.Zero);
mesh_asset = prim.Asset;
}
else
prim.Asset = mesh_asset;
}
}
}
}
}
return Prims;
}
source FindSource(source[] sources, string id)
{
id = id.Substring(1);
foreach (var src in sources)
{
if (src.id == id)
return src;
}
return null;
}
void AddPositions(mesh mesh, ModelPrim prim, Matrix4 transform)
{
prim.Positions = new List<Vector3>();
source posSrc = FindSource(mesh.source, mesh.vertices.input[0].source);
double[] posVals = ((float_array)posSrc.Item).Values;
for (int i = 0; i < posVals.Length / 3; i++)
{
Vector3 pos = new Vector3((float)posVals[i * 3], (float)posVals[i * 3 + 1], (float)posVals[i * 3 + 2]);
pos = Vector3.Transform(pos, transform);
prim.Positions.Add(pos);
}
prim.BoundMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
prim.BoundMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
foreach (var pos in prim.Positions)
{
if (pos.X > prim.BoundMax.X) prim.BoundMax.X = pos.X;
if (pos.Y > prim.BoundMax.Y) prim.BoundMax.Y = pos.Y;
if (pos.Z > prim.BoundMax.Z) prim.BoundMax.Z = pos.Z;
if (pos.X < prim.BoundMin.X) prim.BoundMin.X = pos.X;
if (pos.Y < prim.BoundMin.Y) prim.BoundMin.Y = pos.Y;
if (pos.Z < prim.BoundMin.Z) prim.BoundMin.Z = pos.Z;
}
prim.Scale = prim.BoundMax - prim.BoundMin;
prim.Position = prim.BoundMin + (prim.Scale / 2);
// Fit vertex positions into identity cube -0.5 .. 0.5
for (int i = 0; i < prim.Positions.Count; i++)
{
Vector3 pos = prim.Positions[i];
pos = new Vector3(
prim.Scale.X == 0 ? 0 : ((pos.X - prim.BoundMin.X) / prim.Scale.X) - 0.5f,
prim.Scale.Y == 0 ? 0 : ((pos.Y - prim.BoundMin.Y) / prim.Scale.Y) - 0.5f,
prim.Scale.Z == 0 ? 0 : ((pos.Z - prim.BoundMin.Z) / prim.Scale.Z) - 0.5f
);
prim.Positions[i] = pos;
}
}
int[] StrToArray(string s)
{
string[] vals = Regex.Split(s.Trim(), @"\s+");
int[] ret = new int[vals.Length];
for (int i = 0; i < ret.Length; i++)
{
int.TryParse(vals[i], out ret[i]);
}
return ret;
}
void AddFacesFromPolyList(polylist list, mesh mesh, ModelPrim prim, Matrix4 transform)
{
string material = list.material;
source posSrc = null;
source normalSrc = null;
source uvSrc = null;
ulong stride = 0;
int posOffset = -1;
int norOffset = -1;
int uvOffset = -1;
foreach (var inp in list.input)
{
stride = Math.Max(stride, inp.offset);
if (inp.semantic == "VERTEX")
{
posSrc = FindSource(mesh.source, mesh.vertices.input[0].source);
posOffset = (int)inp.offset;
}
else if (inp.semantic == "NORMAL")
{
normalSrc = FindSource(mesh.source, inp.source);
norOffset = (int)inp.offset;
}
else if (inp.semantic == "TEXCOORD")
{
uvSrc = FindSource(mesh.source, inp.source);
uvOffset = (int)inp.offset;
}
}
stride += 1;
if (posSrc == null) return;
var vcount = StrToArray(list.vcount);
var idx = StrToArray(list.p);
Vector3[] normals = null;
if (normalSrc != null)
{
var norVal = ((float_array)normalSrc.Item).Values;
normals = new Vector3[norVal.Length / 3];
for (int i = 0; i < normals.Length; i++)
{
normals[i] = new Vector3((float)norVal[i * 3 + 0], (float)norVal[i * 3 + 1], (float)norVal[i * 3 + 2]);
normals[i] = Vector3.TransformNormal(normals[i], transform);
normals[i].Normalize();
}
}
Vector2[] uvs = null;
if (uvSrc != null)
{
var uvVal = ((float_array)uvSrc.Item).Values;
uvs = new Vector2[uvVal.Length / 2];
for (int i = 0; i < uvs.Length; i++)
{
uvs[i] = new Vector2((float)uvVal[i * 2 + 0], (float)uvVal[i * 2 + 1]);
}
}
ModelFace face = new ModelFace();
face.MaterialID = list.material;
if (face.MaterialID != null)
{
if (MatSymTarget.ContainsKey(list.material))
{
ModelMaterial mat = Materials.Find(m => m.ID == MatSymTarget[list.material]);
if (mat != null)
{
face.Material = mat;
}
}
}
int curIdx = 0;
for (int i = 0; i < vcount.Length; i++)
{
var nvert = vcount[i];
if (nvert < 3 || nvert > 4)
{
throw new InvalidDataException("Only triangles and quads supported");
}
Vertex[] verts = new Vertex[nvert];
for (int j = 0; j < nvert; j++)
{
verts[j].Position = prim.Positions[idx[curIdx + posOffset + (int)stride * j]];
if (normals != null)
{
verts[j].Normal = normals[idx[curIdx + norOffset + (int)stride * j]];
}
if (uvs != null)
{
verts[j].TexCoord = uvs[idx[curIdx + uvOffset + (int)stride * j]];
}
}
if (nvert == 3) // add the triangle
{
face.AddVertex(verts[0]);
face.AddVertex(verts[1]);
face.AddVertex(verts[2]);
}
else if (nvert == 4) // quad, add two triangles
{
face.AddVertex(verts[0]);
face.AddVertex(verts[1]);
face.AddVertex(verts[2]);
face.AddVertex(verts[0]);
face.AddVertex(verts[2]);
face.AddVertex(verts[3]);
}
curIdx += (int)stride * nvert;
}
prim.Faces.Add(face);
}
polylist Triangles2Polylist(triangles triangles)
{
polylist poly = new polylist();
poly.count = triangles.count;
poly.input = triangles.input;
poly.material = triangles.material;
poly.name = triangles.name;
poly.p = triangles.p;
string str = "3 ";
System.Text.StringBuilder builder = new System.Text.StringBuilder(str.Length * (int)poly.count);
for (int i = 0; i < (int)poly.count; i++) builder.Append(str);
poly.vcount = builder.ToString();
return poly;
}
}
}
+194
View File
@@ -0,0 +1,194 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse.Rendering;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.ImportExport
{
public class ModelMaterial
{
public string ID;
public Color4 DiffuseColor = Color4.White;
public string Texture;
public byte[] TextureData;
}
public class ModelFace
{
public List<Vertex> Vertices = new List<Vertex>();
public List<uint> Indices = new List<uint>();
public string MaterialID = string.Empty;
public ModelMaterial Material = new ModelMaterial();
Dictionary<Vertex, int> LookUp = new Dictionary<Vertex, int>();
public void AddVertex(Vertex v)
{
int index;
if (LookUp.ContainsKey(v))
{
index = LookUp[v];
}
else
{
index = Vertices.Count;
Vertices.Add(v);
LookUp[v] = index;
}
Indices.Add((uint)index);
}
}
public class ModelPrim
{
public List<Vector3> Positions;
public Vector3 BoundMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
public Vector3 BoundMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
public Vector3 Position;
public Vector3 Scale;
public Quaternion Rotation = Quaternion.Identity;
public List<ModelFace> Faces = new List<ModelFace>();
public string ID;
public byte[] Asset;
public void CreateAsset(UUID creator)
{
OSDMap header = new OSDMap();
header["version"] = 1;
header["creator"] = creator;
header["date"] = DateTime.Now;
OSDArray faces = new OSDArray();
foreach (var face in Faces)
{
OSDMap faceMap = new OSDMap();
// Find UV min/max
Vector2 uvMin = new Vector2(float.MaxValue, float.MaxValue);
Vector2 uvMax = new Vector2(float.MinValue, float.MinValue);
foreach (var v in face.Vertices)
{
if (v.TexCoord.X < uvMin.X) uvMin.X = v.TexCoord.X;
if (v.TexCoord.Y < uvMin.Y) uvMin.Y = v.TexCoord.Y;
if (v.TexCoord.X > uvMax.X) uvMax.X = v.TexCoord.X;
if (v.TexCoord.Y > uvMax.Y) uvMax.Y = v.TexCoord.Y;
}
OSDMap uvDomain = new OSDMap();
uvDomain["Min"] = uvMin;
uvDomain["Max"] = uvMax;
faceMap["TexCoord0Domain"] = uvDomain;
OSDMap positionDomain = new OSDMap();
positionDomain["Min"] = new Vector3(-0.5f, -0.5f, -0.5f);
positionDomain["Max"] = new Vector3(0.5f, 0.5f, 0.5f);
faceMap["PositionDomain"] = positionDomain;
List<byte> posBytes = new List<byte>(face.Vertices.Count * sizeof(UInt16) * 3);
List<byte> norBytes = new List<byte>(face.Vertices.Count * sizeof(UInt16) * 3);
List<byte> uvBytes = new List<byte>(face.Vertices.Count * sizeof(UInt16) * 2);
foreach (var v in face.Vertices)
{
posBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Position.X, -0.5f, 0.5f)));
posBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Position.Y, -0.5f, 0.5f)));
posBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Position.Z, -0.5f, 0.5f)));
norBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Normal.X, -1f, 1f)));
norBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Normal.Y, -1f, 1f)));
norBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Normal.Z, -1f, 1f)));
uvBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.TexCoord.X, uvMin.X, uvMax.X)));
uvBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.TexCoord.Y, uvMin.Y, uvMax.Y)));
}
faceMap["Position"] = posBytes.ToArray();
faceMap["Normal"] = norBytes.ToArray();
faceMap["TexCoord0"] = uvBytes.ToArray();
List<byte> indexBytes = new List<byte>(face.Indices.Count * sizeof(UInt16));
foreach (var t in face.Indices)
{
indexBytes.AddRange(Utils.UInt16ToBytes((ushort)t));
}
faceMap["TriangleList"] = indexBytes.ToArray();
faces.Add(faceMap);
}
byte[] physicStubBytes = Helpers.ZCompressOSD(PhysicsStub());
byte[] meshBytes = Helpers.ZCompressOSD(faces);
int n = 0;
OSDMap lodParms = new OSDMap();
lodParms["offset"] = n;
lodParms["size"] = meshBytes.Length;
header["high_lod"] = lodParms;
n += meshBytes.Length;
lodParms = new OSDMap();
lodParms["offset"] = n;
lodParms["size"] = physicStubBytes.Length;
header["physics_convex"] = lodParms;
n += physicStubBytes.Length;
byte[] headerBytes = OSDParser.SerializeLLSDBinary(header, false);
n += headerBytes.Length;
Asset = new byte[n];
int offset = 0;
Buffer.BlockCopy(headerBytes, 0, Asset, offset, headerBytes.Length);
offset += headerBytes.Length;
Buffer.BlockCopy(meshBytes, 0, Asset, offset, meshBytes.Length);
offset += meshBytes.Length;
Buffer.BlockCopy(physicStubBytes, 0, Asset, offset, physicStubBytes.Length);
offset += physicStubBytes.Length;
}
public static OSD PhysicsStub()
{
OSDMap ret = new OSDMap();
ret["Max"] = new Vector3(0.5f, 0.5f, 0.5f);
ret["Min"] = new Vector3(-0.5f, -0.5f, -0.5f);
ret["BoundingVerts"] = new byte[] { 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 127, 0, 0, 255, 255, 255, 127, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 127, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255 };
return ret;
}
}
}
+301
View File
@@ -0,0 +1,301 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text.RegularExpressions;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Http;
namespace OpenMetaverse.ImportExport
{
/// <summary>
/// Implements mesh upload communications with the simulator
/// </summary>
public class ModelUploader
{
/// <summary>
/// Inlcude stub convex hull physics, required for uploading to Second Life
/// </summary>
public bool IncludePhysicsStub;
/// <summary>
/// Use the same mesh used for geometry as the physical mesh upload
/// </summary>
public bool UseModelAsPhysics;
GridClient Client;
List<ModelPrim> Prims;
/// <summary>
/// Callback for mesh upload operations
/// </summary>
/// <param name="result">null on failure, result from server on success</param>
public delegate void ModelUploadCallback(OSD result);
string InvName, InvDescription;
/// <summary>
/// Creates instance of the mesh uploader
/// </summary>
/// <param name="client">GridClient instance to communicate with the simulator</param>
/// <param name="prims">List of ModelPrimitive objects to upload as a linkset</param>
/// <param name="newInvName">Inventory name for newly uploaded object</param>
/// <param name="newInvDesc">Inventory description for newly upload object</param>
public ModelUploader(GridClient client, List<ModelPrim> prims, string newInvName, string newInvDesc)
{
this.Client = client;
this.Prims = prims;
this.InvName = newInvName;
this.InvDescription = newInvDesc;
}
List<byte[]> Images;
Dictionary<string, int> ImgIndex;
OSD AssetResources(bool upload)
{
OSDArray instanceList = new OSDArray();
List<byte[]> meshes = new List<byte[]>();
List<byte[]> textures = new List<byte[]>();
foreach (var prim in Prims)
{
OSDMap primMap = new OSDMap();
OSDArray faceList = new OSDArray();
foreach (var face in prim.Faces)
{
OSDMap faceMap = new OSDMap();
faceMap["diffuse_color"] = face.Material.DiffuseColor;
faceMap["fullbright"] = false;
if (face.Material.TextureData != null)
{
int index;
if (ImgIndex.ContainsKey(face.Material.Texture))
{
index = ImgIndex[face.Material.Texture];
}
else
{
index = Images.Count;
ImgIndex[face.Material.Texture] = index;
Images.Add(face.Material.TextureData);
}
faceMap["image"] = index;
faceMap["scales"] = 1.0f;
faceMap["scalet"] = 1.0f;
faceMap["offsets"] = 0.0f;
faceMap["offsett"] = 0.0f;
faceMap["imagerot"] = 0.0f;
}
faceList.Add(faceMap);
}
primMap["face_list"] = faceList;
primMap["position"] = prim.Position;
primMap["rotation"] = prim.Rotation;
primMap["scale"] = prim.Scale;
primMap["material"] = 3; // always sent as "wood" material
primMap["physics_shape_type"] = 2; // always sent as "convex hull";
primMap["mesh"] = meshes.Count;
meshes.Add(prim.Asset);
instanceList.Add(primMap);
}
OSDMap resources = new OSDMap();
resources["instance_list"] = instanceList;
OSDArray meshList = new OSDArray();
foreach (var mesh in meshes)
{
meshList.Add(OSD.FromBinary(mesh));
}
resources["mesh_list"] = meshList;
OSDArray textureList = new OSDArray();
for (int i = 0; i < Images.Count; i++)
{
if (upload)
{
textureList.Add(new OSDBinary(Images[i]));
}
else
{
textureList.Add(new OSDBinary(Utils.EmptyBytes));
}
}
resources["texture_list"] = textureList;
resources["metric"] = "MUT_Unspecified";
return resources;
}
/// <summary>
/// Performs model upload in one go, without first checking for the price
/// </summary>
public void Upload()
{
Upload(null);
}
/// <summary>
/// Performs model upload in one go, without first checking for the price
/// </summary>
/// <param name="callback">Callback that will be invoke upon completion of the upload. Null is sent on request failure</param>
public void Upload(ModelUploadCallback callback)
{
PrepareUpload((result =>
{
if (result == null && callback != null)
{
callback(null);
return;
}
if (result is OSDMap)
{
var res = (OSDMap)result;
Uri uploader = new Uri(res["uploader"]);
PerformUpload(uploader, (contents =>
{
if (contents != null)
{
var reply = (OSDMap)contents;
if (reply.ContainsKey("new_inventory_item") && reply.ContainsKey("new_asset"))
{
// Request full update on the item in order to update the local store
Client.Inventory.RequestFetchInventory(reply["new_inventory_item"].AsUUID(), Client.Self.AgentID);
}
}
if (callback != null) callback(contents);
}));
}
}));
}
/// <summary>
/// Ask server for details of cost and impact of the mesh upload
/// </summary>
/// <param name="callback">Callback that will be invoke upon completion of the upload. Null is sent on request failure</param>
public void PrepareUpload(ModelUploadCallback callback)
{
Uri url = null;
if (Client.Network.CurrentSim == null ||
Client.Network.CurrentSim.Caps == null ||
null == (url = Client.Network.CurrentSim.Caps.CapabilityURI("NewFileAgentInventory")))
{
Logger.Log("Cannot upload mesh, no connection or NewFileAgentInventory not available", Helpers.LogLevel.Warning);
if (callback != null) callback(null);
return;
}
Images = new List<byte[]>();
ImgIndex = new Dictionary<string, int>();
OSDMap req = new OSDMap();
req["name"] = InvName;
req["description"] = InvDescription;
req["asset_resources"] = AssetResources(false);
req["asset_type"] = "mesh";
req["inventory_type"] = "object";
req["folder_id"] = Client.Inventory.FindFolderForType(AssetType.Object);
req["texture_folder_id"] = Client.Inventory.FindFolderForType(AssetType.Texture);
req["everyone_mask"] = (int)PermissionMask.All;
req["group_mask"] = (int)PermissionMask.All; ;
req["next_owner_mask"] = (int)PermissionMask.All;
CapsClient request = new CapsClient(url);
request.OnComplete += (client, result, error) =>
{
if (error != null || result == null || result.Type != OSDType.Map)
{
Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error);
if (callback != null) callback(null);
return;
}
OSDMap res = (OSDMap)result;
if (res["state"] != "upload")
{
Logger.Log("Mesh upload failure: " + res["message"], Helpers.LogLevel.Error);
if (callback != null) callback(null);
return;
}
Logger.Log("Response from mesh upload prepare:\n" + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug);
if (callback != null) callback(result);
};
request.BeginGetResponse(req, OSDFormat.Xml, 3 * 60 * 1000);
}
/// <summary>
/// Performas actual mesh and image upload
/// </summary>
/// <param name="uploader">Uri recieved in the upload prepare stage</param>
/// <param name="callback">Callback that will be invoke upon completion of the upload. Null is sent on request failure</param>
public void PerformUpload(Uri uploader, ModelUploadCallback callback)
{
CapsClient request = new CapsClient(uploader);
request.OnComplete += (client, result, error) =>
{
if (error != null || result == null || result.Type != OSDType.Map)
{
Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error);
if (callback != null) callback(null);
return;
}
OSDMap res = (OSDMap)result;
Logger.Log("Response from mesh upload perform:\n" + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug);
if (callback != null) callback(res);
};
request.BeginGetResponse(AssetResources(true), OSDFormat.Xml, 60 * 1000);
}
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.Interfaces
{
/// <summary>
/// Interface requirements for Messaging system
/// </summary>
public interface IMessage
{
OSDMap Serialize();
void Deserialize(OSDMap map);
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Drawing;
namespace OpenMetaverse.Rendering
{
[AttributeUsage(AttributeTargets.Class)]
public class RendererNameAttribute : System.Attribute
{
private string _name;
public RendererNameAttribute(string name)
: base()
{
_name = name;
}
public override string ToString()
{
return _name;
}
}
/// <summary>
/// Abstract base for rendering plugins
/// </summary>
public interface IRendering
{
/// <summary>
/// Generates a basic mesh structure from a primitive
/// </summary>
/// <param name="prim">Primitive to generate the mesh from</param>
/// <param name="lod">Level of detail to generate the mesh at</param>
/// <returns>The generated mesh</returns>
SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod);
/// <summary>
/// Generates a basic mesh structure from a sculpted primitive and
/// texture
/// </summary>
/// <param name="prim">Sculpted primitive to generate the mesh from</param>
/// <param name="sculptTexture">Sculpt texture</param>
/// <param name="lod">Level of detail to generate the mesh at</param>
/// <returns>The generated mesh</returns>
SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod);
/// <summary>
/// Generates a series of faces, each face containing a mesh and
/// metadata
/// </summary>
/// <param name="prim">Primitive to generate the mesh from</param>
/// <param name="lod">Level of detail to generate the mesh at</param>
/// <returns>The generated mesh</returns>
FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod);
/// <summary>
/// Generates a series of faces for a sculpted prim, each face
/// containing a mesh and metadata
/// </summary>
/// <param name="prim">Sculpted primitive to generate the mesh from</param>
/// <param name="sculptTexture">Sculpt texture</param>
/// <param name="lod">Level of detail to generate the mesh at</param>
/// <returns>The generated mesh</returns>
FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod);
/// <summary>
/// Apply texture coordinate modifications from a
/// <seealso cref="TextureEntryFace"/> to a list of vertices
/// </summary>
/// <param name="vertices">Vertex list to modify texture coordinates for</param>
/// <param name="center">Center-point of the face</param>
/// <param name="teFace">Face texture parameters</param>
/// <param name="primScale">Scale of the prim</param>
void TransformTexCoords (List<Vertex> vertices, Vector3 center, Primitive.TextureEntryFace teFace, Vector3 primScale);
}
}
+336
View File
@@ -0,0 +1,336 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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;
namespace OpenMetaverse
{
/// <summary>
/// The InternalDictionary class is used through the library for storing key/value pairs.
/// It is intended to be a replacement for the generic Dictionary class and should
/// be used in its place. It contains several methods for allowing access to the data from
/// outside the library that are read only and thread safe.
///
/// </summary>
/// <typeparam name="TKey">Key <see langword="Tkey"/></typeparam>
/// <typeparam name="TValue">Value <see langword="TValue"/></typeparam>
public class InternalDictionary<TKey, TValue>
{
/// <summary>Internal dictionary that this class wraps around. Do not
/// modify or enumerate the contents of this dictionary without locking
/// on this member</summary>
internal Dictionary<TKey, TValue> Dictionary;
public Dictionary<TKey,TValue> Copy()
{
lock (Dictionary)
return new Dictionary<TKey, TValue>(Dictionary);
}
/// <summary>
/// Gets the number of Key/Value pairs contained in the <seealso cref="T:InternalDictionary"/>
/// </summary>
public int Count { get { return Dictionary.Count; } }
/// <summary>
/// Initializes a new instance of the <seealso cref="T:InternalDictionary"/> Class
/// with the specified key/value, has the default initial capacity.
/// </summary>
/// <example>
/// <code>
/// // initialize a new InternalDictionary named testDict with a string as the key and an int as the value.
/// public InternalDictionary&lt;string, int&gt; testDict = new InternalDictionary&lt;string, int&gt;();
/// </code>
/// </example>
public InternalDictionary()
{
Dictionary = new Dictionary<TKey, TValue>();
}
/// <summary>
/// Initializes a new instance of the <seealso cref="T:InternalDictionary"/> Class
/// with the specified key/value, has its initial valies copied from the specified
/// <seealso cref="T:System.Collections.Generic.Dictionary"/>
/// </summary>
/// <param name="dictionary"><seealso cref="T:System.Collections.Generic.Dictionary"/>
/// to copy initial values from</param>
/// <example>
/// <code>
/// // initialize a new InternalDictionary named testAvName with a UUID as the key and an string as the value.
/// // populates with copied values from example KeyNameCache Dictionary.
///
/// // create source dictionary
/// Dictionary&lt;UUID, string&gt; KeyNameCache = new Dictionary&lt;UUID, string&gt;();
/// KeyNameCache.Add("8300f94a-7970-7810-cf2c-fc9aa6cdda24", "Jack Avatar");
/// KeyNameCache.Add("27ba1e40-13f7-0708-3e98-5819d780bd62", "Jill Avatar");
///
/// // Initialize new dictionary.
/// public InternalDictionary&lt;UUID, string&gt; testAvName = new InternalDictionary&lt;UUID, string&gt;(KeyNameCache);
/// </code>
/// </example>
public InternalDictionary(IDictionary<TKey, TValue> dictionary)
{
Dictionary = new Dictionary<TKey, TValue>(dictionary);
}
/// <summary>
/// Initializes a new instance of the <seealso cref="T:OpenMetaverse.InternalDictionary"/> Class
/// with the specified key/value, With its initial capacity specified.
/// </summary>
/// <param name="capacity">Initial size of dictionary</param>
/// <example>
/// <code>
/// // initialize a new InternalDictionary named testDict with a string as the key and an int as the value,
/// // initially allocated room for 10 entries.
/// public InternalDictionary&lt;string, int&gt; testDict = new InternalDictionary&lt;string, int&gt;(10);
/// </code>
/// </example>
public InternalDictionary(int capacity)
{
Dictionary = new Dictionary<TKey, TValue>(capacity);
}
/// <summary>
/// Try to get entry from <seealso cref="T:OpenMetaverse.InternalDictionary"/> with specified key
/// </summary>
/// <param name="key">Key to use for lookup</param>
/// <param name="value">Value returned</param>
/// <returns><see langword="true"/> if specified key exists, <see langword="false"/> if not found</returns>
/// <example>
/// <code>
/// // find your avatar using the Simulator.ObjectsAvatars InternalDictionary:
/// Avatar av;
/// if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(Client.Self.AgentID, out av))
/// Console.WriteLine("Found Avatar {0}", av.Name);
/// </code>
/// <seealso cref="Simulator.ObjectsAvatars"/>
/// </example>
public bool TryGetValue(TKey key, out TValue value)
{
lock (Dictionary)
{
return Dictionary.TryGetValue(key, out value);
}
}
/// <summary>
/// Finds the specified match.
/// </summary>
/// <param name="match">The match.</param>
/// <returns>Matched value</returns>
/// <example>
/// <code>
/// // use a delegate to find a prim in the ObjectsPrimitives InternalDictionary
/// // with the ID 95683496
/// uint findID = 95683496;
/// Primitive findPrim = sim.ObjectsPrimitives.Find(
/// delegate(Primitive prim) { return prim.ID == findID; });
/// </code>
/// </example>
public TValue Find(Predicate<TValue> match)
{
lock (Dictionary)
{
foreach (TValue value in Dictionary.Values)
{
if (match(value))
return value;
}
}
return default(TValue);
}
/// <summary>Find All items in an <seealso cref="T:InternalDictionary"/></summary>
/// <param name="match">return matching items.</param>
/// <returns>a <seealso cref="T:System.Collections.Generic.List"/> containing found items.</returns>
/// <example>
/// Find All prims within 20 meters and store them in a List
/// <code>
/// int radius = 20;
/// List&lt;Primitive&gt; prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(
/// delegate(Primitive prim) {
/// Vector3 pos = prim.Position;
/// return ((prim.ParentID == 0) &amp;&amp; (pos != Vector3.Zero) &amp;&amp; (Vector3.Distance(pos, location) &lt; radius));
/// }
/// );
///</code>
///</example>
public List<TValue> FindAll(Predicate<TValue> match)
{
List<TValue> found = new List<TValue>();
lock (Dictionary)
{
foreach (KeyValuePair<TKey, TValue> kvp in Dictionary)
{
if (match(kvp.Value))
found.Add(kvp.Value);
}
}
return found;
}
/// <summary>Find All items in an <seealso cref="T:InternalDictionary"/></summary>
/// <param name="match">return matching keys.</param>
/// <returns>a <seealso cref="T:System.Collections.Generic.List"/> containing found keys.</returns>
/// <example>
/// Find All keys which also exist in another dictionary
/// <code>
/// List&lt;UUID&gt; matches = myDict.FindAll(
/// delegate(UUID id) {
/// return myOtherDict.ContainsKey(id);
/// }
/// );
///</code>
///</example>
public List<TKey> FindAll(Predicate<TKey> match)
{
List<TKey> found = new List<TKey>();
lock (Dictionary)
{
foreach (KeyValuePair<TKey, TValue> kvp in Dictionary)
{
if (match(kvp.Key))
found.Add(kvp.Key);
}
}
return found;
}
/// <summary>Perform an <seealso cref="T:System.Action"/> on each entry in an <seealso cref="T:OpenMetaverse.InternalDictionary"/></summary>
/// <param name="action"><seealso cref="T:System.Action"/> to perform</param>
/// <example>
/// <code>
/// // Iterates over the ObjectsPrimitives InternalDictionary and prints out some information.
/// Client.Network.CurrentSim.ObjectsPrimitives.ForEach(
/// delegate(Primitive prim)
/// {
/// if (prim.Text != null)
/// {
/// Console.WriteLine("NAME={0} ID = {1} TEXT = '{2}'",
/// prim.PropertiesFamily.Name, prim.ID, prim.Text);
/// }
/// });
///</code>
///</example>
public void ForEach(Action<TValue> action)
{
lock (Dictionary)
{
foreach (TValue value in Dictionary.Values)
{
action(value);
}
}
}
/// <summary>Perform an <seealso cref="T:System.Action"/> on each key of an <seealso cref="T:OpenMetaverse.InternalDictionary"/></summary>
/// <param name="action"><seealso cref="T:System.Action"/> to perform</param>
public void ForEach(Action<TKey> action)
{
lock (Dictionary)
{
foreach (TKey key in Dictionary.Keys)
{
action(key);
}
}
}
/// <summary>
/// Perform an <seealso cref="T:System.Action"/> on each KeyValuePair of an <seealso cref="T:OpenMetaverse.InternalDictionary"/>
/// </summary>
/// <param name="action"><seealso cref="T:System.Action"/> to perform</param>
public void ForEach(Action<KeyValuePair<TKey, TValue>> action)
{
lock (Dictionary)
{
foreach (KeyValuePair<TKey, TValue> entry in Dictionary)
{
action(entry);
}
}
}
/// <summary>Check if Key exists in Dictionary</summary>
/// <param name="key">Key to check for</param>
/// <returns><see langword="true"/> if found, <see langword="false"/> otherwise</returns>
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
/// <summary>Check if Value exists in Dictionary</summary>
/// <param name="value">Value to check for</param>
/// <returns><see langword="true"/> if found, <see langword="false"/> otherwise</returns>
public bool ContainsValue(TValue value)
{
return Dictionary.ContainsValue(value);
}
/// <summary>
/// Adds the specified key to the dictionary, dictionary locking is not performed,
/// <see cref="SafeAdd"/>
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
internal void Add(TKey key, TValue value)
{
lock (Dictionary)
Dictionary.Add(key, value);
}
/// <summary>
/// Removes the specified key, dictionary locking is not performed
/// </summary>
/// <param name="key">The key.</param>
/// <returns><see langword="true"/> if successful, <see langword="false"/> otherwise</returns>
internal bool Remove(TKey key)
{
lock (Dictionary)
return Dictionary.Remove(key);
}
/// <summary>
/// Indexer for the dictionary
/// </summary>
/// <param name="key">The key</param>
/// <returns>The value</returns>
public TValue this[TKey key]
{
get
{
lock (Dictionary)
return Dictionary[key];
}
internal set
{
lock (Dictionary)
Dictionary[key] = value;
}
}
}
}
+580
View File
@@ -0,0 +1,580 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
namespace OpenMetaverse
{
/// <summary>
/// Exception class to identify inventory exceptions
/// </summary>
public class InventoryException : Exception
{
public InventoryException(string message)
: base(message) { }
}
/// <summary>
/// Responsible for maintaining inventory structure. Inventory constructs nodes
/// and manages node children as is necessary to maintain a coherant hirarchy.
/// Other classes should not manipulate or create InventoryNodes explicitly. When
/// A node's parent changes (when a folder is moved, for example) simply pass
/// Inventory the updated InventoryFolder and it will make the appropriate changes
/// to its internal representation.
/// </summary>
public class Inventory
{
/// <summary>The event subscribers, null of no subscribers</summary>
private EventHandler<InventoryObjectUpdatedEventArgs> m_InventoryObjectUpdated;
///<summary>Raises the InventoryObjectUpdated Event</summary>
/// <param name="e">A InventoryObjectUpdatedEventArgs object containing
/// the data sent from the simulator</param>
protected virtual void OnInventoryObjectUpdated(InventoryObjectUpdatedEventArgs e)
{
EventHandler<InventoryObjectUpdatedEventArgs> handler = m_InventoryObjectUpdated;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_InventoryObjectUpdatedLock = new object();
/// <summary>Raised when the simulator sends us data containing
/// ...</summary>
public event EventHandler<InventoryObjectUpdatedEventArgs> InventoryObjectUpdated
{
add { lock (m_InventoryObjectUpdatedLock) { m_InventoryObjectUpdated += value; } }
remove { lock (m_InventoryObjectUpdatedLock) { m_InventoryObjectUpdated -= value; } }
}
/// <summary>The event subscribers, null of no subscribers</summary>
private EventHandler<InventoryObjectRemovedEventArgs> m_InventoryObjectRemoved;
///<summary>Raises the InventoryObjectRemoved Event</summary>
/// <param name="e">A InventoryObjectRemovedEventArgs object containing
/// the data sent from the simulator</param>
protected virtual void OnInventoryObjectRemoved(InventoryObjectRemovedEventArgs e)
{
EventHandler<InventoryObjectRemovedEventArgs> handler = m_InventoryObjectRemoved;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_InventoryObjectRemovedLock = new object();
/// <summary>Raised when the simulator sends us data containing
/// ...</summary>
public event EventHandler<InventoryObjectRemovedEventArgs> InventoryObjectRemoved
{
add { lock (m_InventoryObjectRemovedLock) { m_InventoryObjectRemoved += value; } }
remove { lock (m_InventoryObjectRemovedLock) { m_InventoryObjectRemoved -= value; } }
}
/// <summary>The event subscribers, null of no subscribers</summary>
private EventHandler<InventoryObjectAddedEventArgs> m_InventoryObjectAdded;
///<summary>Raises the InventoryObjectAdded Event</summary>
/// <param name="e">A InventoryObjectAddedEventArgs object containing
/// the data sent from the simulator</param>
protected virtual void OnInventoryObjectAdded(InventoryObjectAddedEventArgs e)
{
EventHandler<InventoryObjectAddedEventArgs> handler = m_InventoryObjectAdded;
if (handler != null)
handler(this, e);
}
/// <summary>Thread sync lock object</summary>
private readonly object m_InventoryObjectAddedLock = new object();
/// <summary>Raised when the simulator sends us data containing
/// ...</summary>
public event EventHandler<InventoryObjectAddedEventArgs> InventoryObjectAdded
{
add { lock (m_InventoryObjectAddedLock) { m_InventoryObjectAdded += value; } }
remove { lock (m_InventoryObjectAddedLock) { m_InventoryObjectAdded -= value; } }
}
/// <summary>
/// The root folder of this avatars inventory
/// </summary>
public InventoryFolder RootFolder
{
get { return RootNode.Data as InventoryFolder; }
set
{
UpdateNodeFor(value);
_RootNode = Items[value.UUID];
}
}
/// <summary>
/// The default shared library folder
/// </summary>
public InventoryFolder LibraryFolder
{
get { return LibraryRootNode.Data as InventoryFolder; }
set
{
UpdateNodeFor(value);
_LibraryRootNode = Items[value.UUID];
}
}
private InventoryNode _LibraryRootNode;
private InventoryNode _RootNode;
/// <summary>
/// The root node of the avatars inventory
/// </summary>
public InventoryNode RootNode
{
get { return _RootNode; }
}
/// <summary>
/// The root node of the default shared library
/// </summary>
public InventoryNode LibraryRootNode
{
get { return _LibraryRootNode; }
}
public UUID Owner {
get { return _Owner; }
}
private UUID _Owner;
private GridClient Client;
//private InventoryManager Manager;
public Dictionary<UUID, InventoryNode> Items = new Dictionary<UUID, InventoryNode>();
public Inventory(GridClient client, InventoryManager manager)
: this(client, manager, client.Self.AgentID) { }
public Inventory(GridClient client, InventoryManager manager, UUID owner)
{
Client = client;
//Manager = manager;
_Owner = owner;
if (owner == UUID.Zero)
Logger.Log("Inventory owned by nobody!", Helpers.LogLevel.Warning, Client);
Items = new Dictionary<UUID, InventoryNode>();
}
public List<InventoryBase> GetContents(InventoryFolder folder)
{
return GetContents(folder.UUID);
}
/// <summary>
/// Returns the contents of the specified folder
/// </summary>
/// <param name="folder">A folder's UUID</param>
/// <returns>The contents of the folder corresponding to <code>folder</code></returns>
/// <exception cref="InventoryException">When <code>folder</code> does not exist in the inventory</exception>
public List<InventoryBase> GetContents(UUID folder)
{
InventoryNode folderNode;
if (!Items.TryGetValue(folder, out folderNode))
throw new InventoryException("Unknown folder: " + folder);
lock (folderNode.Nodes.SyncRoot)
{
List<InventoryBase> contents = new List<InventoryBase>(folderNode.Nodes.Count);
foreach (InventoryNode node in folderNode.Nodes.Values)
{
contents.Add(node.Data);
}
return contents;
}
}
/// <summary>
/// Updates the state of the InventoryNode and inventory data structure that
/// is responsible for the InventoryObject. If the item was previously not added to inventory,
/// it adds the item, and updates structure accordingly. If it was, it updates the
/// InventoryNode, changing the parent node if <code>item.parentUUID</code> does
/// not match <code>node.Parent.Data.UUID</code>.
///
/// You can not set the inventory root folder using this method
/// </summary>
/// <param name="item">The InventoryObject to store</param>
public void UpdateNodeFor(InventoryBase item)
{
lock (Items)
{
InventoryNode itemParent = null;
if (item.ParentUUID != UUID.Zero && !Items.TryGetValue(item.ParentUUID, out itemParent))
{
// OK, we have no data on the parent, let's create a fake one.
InventoryFolder fakeParent = new InventoryFolder(item.ParentUUID);
fakeParent.DescendentCount = 1; // Dear god, please forgive me.
itemParent = new InventoryNode(fakeParent);
Items[item.ParentUUID] = itemParent;
// Unfortunately, this breaks the nice unified tree
// while we're waiting for the parent's data to come in.
// As soon as we get the parent, the tree repairs itself.
//Logger.DebugLog("Attempting to update inventory child of " +
// item.ParentUUID.ToString() + " when we have no local reference to that folder", Client);
if (Client.Settings.FETCH_MISSING_INVENTORY)
{
// Fetch the parent
List<UUID> fetchreq = new List<UUID>(1);
fetchreq.Add(item.ParentUUID);
}
}
InventoryNode itemNode;
if (Items.TryGetValue(item.UUID, out itemNode)) // We're updating.
{
InventoryNode oldParent = itemNode.Parent;
// Handle parent change
if (oldParent == null || itemParent == null || itemParent.Data.UUID != oldParent.Data.UUID)
{
if (oldParent != null)
{
lock (oldParent.Nodes.SyncRoot)
oldParent.Nodes.Remove(item.UUID);
}
if (itemParent != null)
{
lock (itemParent.Nodes.SyncRoot)
itemParent.Nodes[item.UUID] = itemNode;
}
}
itemNode.Parent = itemParent;
if (m_InventoryObjectUpdated != null)
{
OnInventoryObjectUpdated(new InventoryObjectUpdatedEventArgs(itemNode.Data, item));
}
itemNode.Data = item;
}
else // We're adding.
{
itemNode = new InventoryNode(item, itemParent);
Items.Add(item.UUID, itemNode);
if (m_InventoryObjectAdded != null)
{
OnInventoryObjectAdded(new InventoryObjectAddedEventArgs(item));
}
}
}
}
public InventoryNode GetNodeFor(UUID uuid)
{
return Items[uuid];
}
/// <summary>
/// Removes the InventoryObject and all related node data from Inventory.
/// </summary>
/// <param name="item">The InventoryObject to remove.</param>
public void RemoveNodeFor(InventoryBase item)
{
lock (Items)
{
InventoryNode node;
if (Items.TryGetValue(item.UUID, out node))
{
if (node.Parent != null)
lock (node.Parent.Nodes.SyncRoot)
node.Parent.Nodes.Remove(item.UUID);
Items.Remove(item.UUID);
if (m_InventoryObjectRemoved != null)
{
OnInventoryObjectRemoved(new InventoryObjectRemovedEventArgs(item));
}
}
// In case there's a new parent:
InventoryNode newParent;
if (Items.TryGetValue(item.ParentUUID, out newParent))
{
lock (newParent.Nodes.SyncRoot)
newParent.Nodes.Remove(item.UUID);
}
}
}
/// <summary>
/// Used to find out if Inventory contains the InventoryObject
/// specified by <code>uuid</code>.
/// </summary>
/// <param name="uuid">The UUID to check.</param>
/// <returns>true if inventory contains uuid, false otherwise</returns>
public bool Contains(UUID uuid)
{
return Items.ContainsKey(uuid);
}
public bool Contains(InventoryBase obj)
{
return Contains(obj.UUID);
}
/// <summary>
/// Saves the current inventory structure to a cache file
/// </summary>
/// <param name="filename">Name of the cache file to save to</param>
public void SaveToDisk(string filename)
{
try
{
using (Stream stream = File.Open(filename, FileMode.Create))
{
BinaryFormatter bformatter = new BinaryFormatter();
lock (Items)
{
Logger.Log("Caching " + Items.Count.ToString() + " inventory items to " + filename, Helpers.LogLevel.Info);
foreach (KeyValuePair<UUID, InventoryNode> kvp in Items)
{
bformatter.Serialize(stream, kvp.Value);
}
}
}
}
catch (Exception e)
{
Logger.Log("Error saving inventory cache to disk :"+e.Message,Helpers.LogLevel.Error);
}
}
/// <summary>
/// Loads in inventory cache file into the inventory structure. Note only valid to call after login has been successful.
/// </summary>
/// <param name="filename">Name of the cache file to load</param>
/// <returns>The number of inventory items sucessfully reconstructed into the inventory node tree</returns>
public int RestoreFromDisk(string filename)
{
List<InventoryNode> nodes = new List<InventoryNode>();
int item_count = 0;
try
{
if (!File.Exists(filename))
return -1;
using (Stream stream = File.Open(filename, FileMode.Open))
{
BinaryFormatter bformatter = new BinaryFormatter();
while (stream.Position < stream.Length)
{
OpenMetaverse.InventoryNode node = (InventoryNode)bformatter.Deserialize(stream);
nodes.Add(node);
item_count++;
}
}
}
catch (Exception e)
{
Logger.Log("Error accessing inventory cache file :" + e.Message, Helpers.LogLevel.Error);
return -1;
}
Logger.Log("Read " + item_count.ToString() + " items from inventory cache file", Helpers.LogLevel.Info);
item_count = 0;
List<InventoryNode> del_nodes = new List<InventoryNode>(); //nodes that we have processed and will delete
List<UUID> dirty_folders = new List<UUID>(); // Tainted folders that we will not restore items into
// Because we could get child nodes before parents we must itterate around and only add nodes who have
// a parent already in the list because we must update both child and parent to link together
// But sometimes we have seen orphin nodes due to bad/incomplete data when caching so we have an emergency abort route
int stuck = 0;
while (nodes.Count != 0 && stuck<5)
{
foreach (InventoryNode node in nodes)
{
InventoryNode pnode;
if (node.ParentID == UUID.Zero)
{
//We don't need the root nodes "My Inventory" etc as they will already exist for the correct
// user of this cache.
del_nodes.Add(node);
item_count--;
}
else if(Items.TryGetValue(node.Data.UUID,out pnode))
{
//We already have this it must be a folder
if (node.Data is InventoryFolder)
{
InventoryFolder cache_folder = (InventoryFolder)node.Data;
InventoryFolder server_folder = (InventoryFolder)pnode.Data;
if (cache_folder.Version != server_folder.Version)
{
Logger.DebugLog("Inventory Cache/Server version mismatch on " + node.Data.Name + " " + cache_folder.Version.ToString() + " vs " + server_folder.Version.ToString());
pnode.NeedsUpdate = true;
dirty_folders.Add(node.Data.UUID);
}
else
{
pnode.NeedsUpdate = false;
}
del_nodes.Add(node);
}
}
else if (Items.TryGetValue(node.ParentID, out pnode))
{
if (node.Data != null)
{
// If node is folder, and it does not exist in skeleton, mark it as
// dirty and don't process nodes that belong to it
if (node.Data is InventoryFolder && !(Items.ContainsKey(node.Data.UUID)))
{
dirty_folders.Add(node.Data.UUID);
}
//Only add new items, this is most likely to be run at login time before any inventory
//nodes other than the root are populated. Don't add non existing folders.
if (!Items.ContainsKey(node.Data.UUID) && !dirty_folders.Contains(pnode.Data.UUID) && !(node.Data is InventoryFolder))
{
Items.Add(node.Data.UUID, node);
node.Parent = pnode; //Update this node with its parent
pnode.Nodes.Add(node.Data.UUID, node); // Add to the parents child list
item_count++;
}
}
del_nodes.Add(node);
}
}
if (del_nodes.Count == 0)
stuck++;
else
stuck = 0;
//Clean up processed nodes this loop around.
foreach (InventoryNode node in del_nodes)
nodes.Remove(node);
del_nodes.Clear();
}
Logger.Log("Reassembled " + item_count.ToString() + " items from inventory cache file", Helpers.LogLevel.Info);
return item_count;
}
#region Operators
/// <summary>
/// By using the bracket operator on this class, the program can get the
/// InventoryObject designated by the specified uuid. If the value for the corresponding
/// UUID is null, the call is equivelant to a call to <code>RemoveNodeFor(this[uuid])</code>.
/// If the value is non-null, it is equivelant to a call to <code>UpdateNodeFor(value)</code>,
/// the uuid parameter is ignored.
/// </summary>
/// <param name="uuid">The UUID of the InventoryObject to get or set, ignored if set to non-null value.</param>
/// <returns>The InventoryObject corresponding to <code>uuid</code>.</returns>
public InventoryBase this[UUID uuid]
{
get
{
InventoryNode node = Items[uuid];
return node.Data;
}
set
{
if (value != null)
{
// Log a warning if there is a UUID mismatch, this will cause problems
if (value.UUID != uuid)
Logger.Log("Inventory[uuid]: uuid " + uuid.ToString() + " is not equal to value.UUID " +
value.UUID.ToString(), Helpers.LogLevel.Warning, Client);
UpdateNodeFor(value);
}
else
{
InventoryNode node;
if (Items.TryGetValue(uuid, out node))
{
RemoveNodeFor(node.Data);
}
}
}
}
#endregion Operators
}
#region EventArgs classes
public class InventoryObjectUpdatedEventArgs : EventArgs
{
private readonly InventoryBase m_OldObject;
private readonly InventoryBase m_NewObject;
public InventoryBase OldObject { get { return m_OldObject; } }
public InventoryBase NewObject { get { return m_NewObject; } }
public InventoryObjectUpdatedEventArgs(InventoryBase oldObject, InventoryBase newObject)
{
this.m_OldObject = oldObject;
this.m_NewObject = newObject;
}
}
public class InventoryObjectRemovedEventArgs : EventArgs
{
private readonly InventoryBase m_Obj;
public InventoryBase Obj { get { return m_Obj; } }
public InventoryObjectRemovedEventArgs(InventoryBase obj)
{
this.m_Obj = obj;
}
}
public class InventoryObjectAddedEventArgs : EventArgs
{
private readonly InventoryBase m_Obj;
public InventoryBase Obj { get { return m_Obj; } }
public InventoryObjectAddedEventArgs(InventoryBase obj)
{
this.m_Obj = obj;
}
}
#endregion EventArgs
}
File diff suppressed because it is too large Load Diff
+190
View File
@@ -0,0 +1,190 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
using System.Runtime.Serialization;
namespace OpenMetaverse
{
[Serializable()]
public class InventoryNode : ISerializable
{
private InventoryBase data;
private InventoryNode parent;
private UUID parentID; //used for de-seralization
private InventoryNodeDictionary nodes;
private bool needsUpdate = true;
[NonSerialized]
private object tag;
/// <summary></summary>
public InventoryBase Data
{
get { return data; }
set { data = value; }
}
/// <summary>User data</summary>
public object Tag
{
get { return tag; }
set { tag = value; }
}
/// <summary></summary>
public InventoryNode Parent
{
get { return parent; }
set { parent = value; }
}
/// <summary></summary>
public UUID ParentID
{
get { return parentID; }
}
/// <summary></summary>
public InventoryNodeDictionary Nodes
{
get
{
if (nodes == null)
nodes = new InventoryNodeDictionary(this);
return nodes;
}
set { nodes = value; }
}
public System.DateTime ModifyTime
{
get
{
if (Data is InventoryItem)
{
return ((InventoryItem)Data).CreationDate;
}
DateTime newest = default(DateTime);//.MinValue;
if (Data is InventoryFolder)
{
foreach (var node in Nodes.Values)
{
var t = node.ModifyTime;
if (t > newest) newest = t;
}
}
return newest;
}
}
public void Sort()
{
Nodes.Sort();
}
/// <summary>
/// For inventory folder nodes specifies weather the folder needs to be
/// refreshed from the server
/// </summary>
public bool NeedsUpdate
{
get { return needsUpdate; }
set { needsUpdate = value; }
}
/// <summary>
///
/// </summary>
public InventoryNode()
{
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
public InventoryNode(InventoryBase data)
{
this.data = data;
}
/// <summary>
/// De-serialization constructor for the InventoryNode Class
/// </summary>
public InventoryNode(InventoryBase data, InventoryNode parent)
{
this.data = data;
this.parent = parent;
if (parent != null)
{
// Add this node to the collection of parent nodes
lock (parent.Nodes.SyncRoot) parent.Nodes.Add(data.UUID, this);
}
}
/// <summary>
/// Serialization handler for the InventoryNode Class
/// </summary>
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
if(parent!=null)
info.AddValue("Parent", parent.Data.UUID, typeof(UUID)); //We need to track the parent UUID for de-serialization
else
info.AddValue("Parent", UUID.Zero, typeof(UUID));
info.AddValue("Type", data.GetType(), typeof(Type));
data.GetObjectData(info, ctxt);
}
/// <summary>
/// De-serialization handler for the InventoryNode Class
/// </summary>
public InventoryNode(SerializationInfo info, StreamingContext ctxt)
{
parentID = (UUID)info.GetValue("Parent", typeof(UUID));
Type type = (Type)info.GetValue("Type", typeof(Type));
// Construct a new inventory object based on the Type stored in Type
System.Reflection.ConstructorInfo ctr = type.GetConstructor(new Type[] {typeof(SerializationInfo),typeof(StreamingContext)});
data = (InventoryBase) ctr.Invoke(new Object[] { info, ctxt });
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (this.Data == null) return "[Empty Node]";
return this.Data.ToString();
}
}
}
+162
View File
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
namespace OpenMetaverse
{
public class InventoryNodeDictionary: IComparer<UUID>
{
protected SortedDictionary<UUID, InventoryNode> SDictionary;
protected Dictionary<UUID, InventoryNode> Dictionary = new Dictionary<UUID, InventoryNode>();
protected InventoryNode parent;
protected object syncRoot = new object();
public int Compare(UUID id1, UUID id2)
{
InventoryNode n1 = Get(id1);
InventoryNode n2 = Get(id2);
int diff = NullCompare(n1, n2);
if (diff != 0) return diff;
if (n1 == null) return id1.CompareTo(id2);
DateTime t1 = n1.ModifyTime;
DateTime t2 = n2.ModifyTime;
diff = t1.CompareTo(t2);
if (diff != 0) return diff;
var d1 = n1.Data;
var d2 = n2.Data;
diff = NullCompare(d1, d2);
if (diff != 0) return diff;
if (d1 != null)
{
diff = NullCompare(d1.Name, d2.Name);
if (diff != 0) return diff;
if (d1.Name != null)
{
// both are not null.. due to NullCoimpare code
diff = d1.Name.CompareTo(d2.Name);
if (diff != 0) return diff;
}
}
return id1.CompareTo(id2);
}
private InventoryNode Get(UUID uuid)
{
InventoryNode val;
if (Dictionary.TryGetValue(uuid, out val))
{
return val;
}
return null;
}
static int NullCompare(object o1, object o2)
{
return ReferenceEquals(o1, null).CompareTo(ReferenceEquals(o2, null));
}
public InventoryNode Parent
{
get { return parent; }
set { parent = value; }
}
public object SyncRoot { get { return syncRoot; } }
public int Count { get { return Dictionary.Count; } }
public InventoryNodeDictionary(InventoryNode parentNode)
{
if (Settings.SORT_INVENTORY) SDictionary = new SortedDictionary<UUID, InventoryNode>(this);
parent = parentNode;
}
public InventoryNode this[UUID key]
{
get { return (InventoryNode)this.Dictionary[key]; }
set
{
value.Parent = parent;
lock (syncRoot)
{
Dictionary[key] = value;
if (Settings.SORT_INVENTORY) this.SDictionary[key] = value;
}
}
}
public ICollection<UUID> Keys
{
get
{
if (Settings.SORT_INVENTORY) return this.SDictionary.Keys;
return Dictionary.Keys;
}
}
public ICollection<InventoryNode> Values
{
get
{
if (Settings.SORT_INVENTORY) return this.SDictionary.Values;
return this.Dictionary.Values;
}
}
public void Add(UUID key, InventoryNode value)
{
value.Parent = parent;
lock (syncRoot)
{
Dictionary[key] = value;
if (Settings.SORT_INVENTORY) this.SDictionary.Add(key, value);
}
}
public void Remove(UUID key)
{
lock (syncRoot)
{
this.Dictionary.Remove(key);
if (Settings.SORT_INVENTORY) this.SDictionary.Remove(key);
}
}
public bool Contains(UUID key)
{
return this.Dictionary.ContainsKey(key);
}
internal void Sort()
{
if (Settings.SORT_INVENTORY)
{
// TODO resort SDictionary now that more data has come?
}
}
}
}
+187
View File
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 log4net;
using log4net.Config;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace OpenMetaverse
{
/// <summary>
/// Singleton logging class for the entire library
/// </summary>
public static class Logger
{
/// <summary>
/// Callback used for client apps to receive log messages from
/// the library
/// </summary>
/// <param name="message">Data being logged</param>
/// <param name="level">The severity of the log entry from <seealso cref="Helpers.LogLevel"/></param>
public delegate void LogCallback(object message, Helpers.LogLevel level);
/// <summary>Triggered whenever a message is logged. If this is left
/// null, log messages will go to the console</summary>
public static event LogCallback OnLogMessage;
/// <summary>log4net logging engine</summary>
public static ILog LogInstance;
/// <summary>
/// Default constructor
/// </summary>
static Logger()
{
LogInstance = LogManager.GetLogger("OpenMetaverse");
// If error level reporting isn't enabled we assume no logger is configured and initialize a default
// ConsoleAppender
if (!LogInstance.Logger.IsEnabledFor(log4net.Core.Level.Error))
{
log4net.Appender.ConsoleAppender appender = new log4net.Appender.ConsoleAppender();
appender.Layout = new log4net.Layout.PatternLayout("%timestamp [%thread] %-5level - %message%newline");
BasicConfigurator.Configure(appender);
if(Settings.LOG_LEVEL != Helpers.LogLevel.None)
LogInstance.Info("No log configuration found, defaulting to console logging");
}
}
/// <summary>
/// Send a log message to the logging engine
/// </summary>
/// <param name="message">The log message</param>
/// <param name="level">The severity of the log entry</param>
public static void Log(object message, Helpers.LogLevel level)
{
Log(message, level, null, null);
}
/// <summary>
/// Send a log message to the logging engine
/// </summary>
/// <param name="message">The log message</param>
/// <param name="level">The severity of the log entry</param>
/// <param name="client">Instance of the client</param>
public static void Log(object message, Helpers.LogLevel level, GridClient client)
{
Log(message, level, client, null);
}
/// <summary>
/// Send a log message to the logging engine
/// </summary>
/// <param name="message">The log message</param>
/// <param name="level">The severity of the log entry</param>
/// <param name="exception">Exception that was raised</param>
public static void Log(object message, Helpers.LogLevel level, Exception exception)
{
Log(message, level, null, exception);
}
/// <summary>
/// Send a log message to the logging engine
/// </summary>
/// <param name="message">The log message</param>
/// <param name="level">The severity of the log entry</param>
/// <param name="client">Instance of the client</param>
/// <param name="exception">Exception that was raised</param>
public static void Log(object message, Helpers.LogLevel level, GridClient client, Exception exception)
{
if (client != null && client.Settings.LOG_NAMES)
message = String.Format("<{0}>: {1}", client.Self.Name, message);
if (OnLogMessage != null)
OnLogMessage(message, level);
switch (level)
{
case Helpers.LogLevel.Debug:
if (Settings.LOG_LEVEL == Helpers.LogLevel.Debug)
LogInstance.Debug(message, exception);
break;
case Helpers.LogLevel.Info:
if (Settings.LOG_LEVEL == Helpers.LogLevel.Debug
|| Settings.LOG_LEVEL == Helpers.LogLevel.Info)
LogInstance.Info(message, exception);
break;
case Helpers.LogLevel.Warning:
if (Settings.LOG_LEVEL == Helpers.LogLevel.Debug
|| Settings.LOG_LEVEL == Helpers.LogLevel.Info
|| Settings.LOG_LEVEL == Helpers.LogLevel.Warning)
LogInstance.Warn(message, exception);
break;
case Helpers.LogLevel.Error:
if (Settings.LOG_LEVEL == Helpers.LogLevel.Debug
|| Settings.LOG_LEVEL == Helpers.LogLevel.Info
|| Settings.LOG_LEVEL == Helpers.LogLevel.Warning
|| Settings.LOG_LEVEL == Helpers.LogLevel.Error)
LogInstance.Error(message, exception);
break;
default:
break;
}
}
/// <summary>
/// If the library is compiled with DEBUG defined, an event will be
/// fired if an <code>OnLogMessage</code> handler is registered and the
/// message will be sent to the logging engine
/// </summary>
/// <param name="message">The message to log at the DEBUG level to the
/// current logging engine</param>
public static void DebugLog(object message)
{
DebugLog(message, null);
}
/// <summary>
/// If the library is compiled with DEBUG defined and
/// <code>GridClient.Settings.DEBUG</code> is true, an event will be
/// fired if an <code>OnLogMessage</code> handler is registered and the
/// message will be sent to the logging engine
/// </summary>
/// <param name="message">The message to log at the DEBUG level to the
/// current logging engine</param>
/// <param name="client">Instance of the client</param>
[System.Diagnostics.Conditional("DEBUG")]
public static void DebugLog(object message, GridClient client)
{
if (Settings.LOG_LEVEL == Helpers.LogLevel.Debug)
{
if (client != null && client.Settings.LOG_NAMES)
message = String.Format("<{0}>: {1}", client.Self.Name, message);
if (OnLogMessage != null)
OnLogMessage(message, Helpers.LogLevel.Debug);
LogInstance.Debug(message);
}
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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 OpenMetaverse.StructuredData;
using OpenMetaverse.Interfaces;
using OpenMetaverse.Messages.Linden;
namespace OpenMetaverse.Messages
{
public static partial class MessageUtils
{
/// <summary>
/// Return a decoded capabilities message as a strongly typed object
/// </summary>
/// <param name="eventName">A string containing the name of the capabilities message key</param>
/// <param name="map">An <see cref="OSDMap"/> to decode</param>
/// <returns>A strongly typed object containing the decoded information from the capabilities message, or null
/// if no existing Message object exists for the specified event</returns>
public static IMessage DecodeEvent(string eventName, OSDMap map)
{
IMessage message = null;
switch (eventName)
{
case "AgentGroupDataUpdate": message = new AgentGroupDataUpdateMessage(); break;
case "AvatarGroupsReply": message = new AgentGroupDataUpdateMessage(); break; // OpenSim sends the above with the wrong? key
case "ParcelProperties": message = new ParcelPropertiesMessage(); break;
case "ParcelObjectOwnersReply": message = new ParcelObjectOwnersReplyMessage(); break;
case "TeleportFinish": message = new TeleportFinishMessage(); break;
case "EnableSimulator": message = new EnableSimulatorMessage(); break;
case "ParcelPropertiesUpdate": message = new ParcelPropertiesUpdateMessage(); break;
case "EstablishAgentCommunication": message = new EstablishAgentCommunicationMessage(); break;
case "ChatterBoxInvitation": message = new ChatterBoxInvitationMessage(); break;
case "ChatterBoxSessionEventReply": message = new ChatterboxSessionEventReplyMessage(); break;
case "ChatterBoxSessionStartReply": message = new ChatterBoxSessionStartReplyMessage(); break;
case "ChatterBoxSessionAgentListUpdates": message = new ChatterBoxSessionAgentListUpdatesMessage(); break;
case "RequiredVoiceVersion": message = new RequiredVoiceVersionMessage(); break;
case "MapLayer": message = new MapLayerMessage(); break;
case "ChatSessionRequest": message = new ChatSessionRequestMessage(); break;
case "CopyInventoryFromNotecard": message = new CopyInventoryFromNotecardMessage(); break;
case "ProvisionVoiceAccountRequest": message = new ProvisionVoiceAccountRequestMessage(); break;
case "Viewerstats": message = new ViewerStatsMessage(); break;
case "UpdateAgentLanguage": message = new UpdateAgentLanguageMessage(); break;
case "RemoteParcelRequest": message = new RemoteParcelRequestMessage(); break;
case "UpdateScriptTask": message = new UpdateScriptTaskMessage(); break;
case "UpdateScriptAgent": message = new UpdateScriptAgentMessage(); break;
case "SendPostcard": message = new SendPostcardMessage(); break;
case "UpdateGestureAgentInventory": message = new UpdateGestureAgentInventoryMessage(); break;
case "UpdateNotecardAgentInventory": message = new UpdateNotecardAgentInventoryMessage(); break;
case "LandStatReply": message = new LandStatReplyMessage(); break;
case "ParcelVoiceInfoRequest": message = new ParcelVoiceInfoRequestMessage(); break;
case "ViewerStats": message = new ViewerStatsMessage(); break;
case "EventQueueGet": message = new EventQueueGetMessage(); break;
case "CrossedRegion": message = new CrossedRegionMessage(); break;
case "TeleportFailed": message = new TeleportFailedMessage(); break;
case "PlacesReply": message = new PlacesReplyMessage(); break;
case "UpdateAgentInformation": message = new UpdateAgentInformationMessage(); break;
case "DirLandReply": message = new DirLandReplyMessage(); break;
case "ScriptRunningReply": message = new ScriptRunningReplyMessage(); break;
case "SearchStatRequest": message = new SearchStatRequestMessage(); break;
case "AgentDropGroup": message = new AgentDropGroupMessage(); break;
case "AgentStateUpdate": message = new AgentStateUpdateMessage(); break;
case "ForceCloseChatterBoxSession": message = new ForceCloseChatterBoxSessionMessage(); break;
case "UploadBakedTexture": message = new UploadBakedTextureMessage(); break;
case "RegionInfo": message = new RegionInfoMessage(); break;
case "ObjectMediaNavigate": message = new ObjectMediaNavigateMessage(); break;
case "ObjectMedia": message = new ObjectMediaMessage(); break;
case "AttachmentResources": message = AttachmentResourcesMessage.GetMessageHandler(map); break;
case "LandResources": message = LandResourcesMessage.GetMessageHandler(map); break;
case "GetDisplayNames": message = new GetDisplayNamesMessage(); break;
case "SetDisplayName": message = new SetDisplayNameMessage(); break;
case "SetDisplayNameReply": message = new SetDisplayNameReplyMessage(); break;
case "DisplayNameUpdate": message = new DisplayNameUpdateMessage(); break;
//case "ProductInfoRequest": message = new ProductInfoRequestMessage(); break;
case "ObjectPhysicsProperties": message = new ObjectPhysicsPropertiesMessage(); break;
case "BulkUpdateInventory": message = new BulkUpdateInventoryMessage(); break;
case "RenderMaterials": message = new RenderMaterialsMessage(); break;
// Capabilities TODO:
// DispatchRegionInfo
// EstateChangeInfo
// EventQueueGet
// FetchInventoryDescendents
// GroupProposalBallot
// MapLayerGod
// NewFileAgentInventory
// RequestTextureDownload
// SearchStatRequest
// SearchStatTracking
// SendUserReport
// SendUserReportWithScreenshot
// ServerReleaseNotes
// StartGroupProposal
// UpdateGestureTaskInventory
// UpdateNotecardTaskInventory
// ViewerStartAuction
// UntrustedSimulatorMessage
}
if (message != null)
{
try
{
message.Deserialize(map);
return message;
}
catch (Exception e)
{
Logger.Log("Exception while trying to Deserialize " + eventName + ":" + e.Message + ": " + e.StackTrace, Helpers.LogLevel.Error);
}
return null;
}
else
{
return null;
}
}
}
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using OpenMetaverse.StructuredData;
namespace OpenMetaverse.Messages
{
public static partial class MessageUtils
{
public static IPAddress ToIP(OSD osd)
{
byte[] binary = osd.AsBinary();
if (binary != null && binary.Length == 4)
return new IPAddress(binary);
else
return IPAddress.Any;
}
public static OSD FromIP(IPAddress address)
{
if (address != null && address != IPAddress.Any)
return OSD.FromBinary(address.GetAddressBytes());
else
return new OSD();
}
public static Dictionary<string, string> ToDictionaryString(OSD osd)
{
if (osd.Type == OSDType.Map)
{
OSDMap map = (OSDMap)osd;
Dictionary<string, string> dict = new Dictionary<string, string>(map.Count);
foreach (KeyValuePair<string, OSD> entry in map)
dict.Add(entry.Key, entry.Value.AsString());
return dict;
}
return new Dictionary<string, string>(0);
}
public static Dictionary<Uri, Uri> ToDictionaryUri(OSD osd)
{
if (osd.Type == OSDType.Map)
{
OSDMap map = (OSDMap)osd;
Dictionary<Uri, Uri> dict = new Dictionary<Uri, Uri>(map.Count);
foreach (KeyValuePair<string, OSD> entry in map)
dict.Add(new Uri(entry.Key), entry.Value.AsUri());
return dict;
}
return new Dictionary<Uri, Uri>(0);
}
public static OSDMap FromDictionaryString(Dictionary<string, string> dict)
{
if (dict != null)
{
OSDMap map = new OSDMap(dict.Count);
foreach (KeyValuePair<string, string> entry in dict)
map.Add(entry.Key, OSD.FromString(entry.Value));
return map;
}
return new OSDMap(0);
}
public static OSDMap FromDictionaryUri(Dictionary<Uri, Uri> dict)
{
if (dict != null)
{
OSDMap map = new OSDMap(dict.Count);
foreach (KeyValuePair<Uri, Uri> entry in dict)
map.Add(entry.Key.ToString(), OSD.FromUri(entry.Value));
return map;
}
return new OSDMap(0);
}
}
}
+342
View File
@@ -0,0 +1,342 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Text;
namespace OpenMetaverse
{
/// <summary>
/// A Name Value pair with additional settings, used in the protocol
/// primarily to transmit avatar names and active group in object packets
/// </summary>
public struct NameValue
{
#region Enums
/// <summary>Type of the value</summary>
public enum ValueType
{
/// <summary>Unknown</summary>
Unknown = -1,
/// <summary>String value</summary>
String,
/// <summary></summary>
F32,
/// <summary></summary>
S32,
/// <summary></summary>
VEC3,
/// <summary></summary>
U32,
/// <summary>Deprecated</summary>
[Obsolete]
CAMERA,
/// <summary>String value, but designated as an asset</summary>
Asset,
/// <summary></summary>
U64
}
/// <summary>
///
/// </summary>
public enum ClassType
{
/// <summary></summary>
Unknown = -1,
/// <summary></summary>
ReadOnly,
/// <summary></summary>
ReadWrite,
/// <summary></summary>
Callback
}
/// <summary>
///
/// </summary>
public enum SendtoType
{
/// <summary></summary>
Unknown = -1,
/// <summary></summary>
Sim,
/// <summary></summary>
DataSim,
/// <summary></summary>
SimViewer,
/// <summary></summary>
DataSimViewer
}
#endregion Enums
/// <summary></summary>
public string Name;
/// <summary></summary>
public ValueType Type;
/// <summary></summary>
public ClassType Class;
/// <summary></summary>
public SendtoType Sendto;
/// <summary></summary>
public object Value;
private static readonly string[] TypeStrings = new string[]
{
"STRING",
"F32",
"S32",
"VEC3",
"U32",
"ASSET",
"U64"
};
private static readonly string[] ClassStrings = new string[]
{
"R", // Read-only
"RW", // Read-write
"CB" // Callback
};
private static readonly string[] SendtoStrings = new string[]
{
"S", // Sim
"DS", // Data Sim
"SV", // Sim Viewer
"DSV" // Data Sim Viewer
};
private static readonly char[] Separators = new char[]
{
' ',
'\n',
'\t',
'\r'
};
/// <summary>
/// Constructor that takes all the fields as parameters
/// </summary>
/// <param name="name"></param>
/// <param name="valueType"></param>
/// <param name="classType"></param>
/// <param name="sendtoType"></param>
/// <param name="value"></param>
public NameValue(string name, ValueType valueType, ClassType classType, SendtoType sendtoType, object value)
{
Name = name;
Type = valueType;
Class = classType;
Sendto = sendtoType;
Value = value;
}
/// <summary>
/// Constructor that takes a single line from a NameValue field
/// </summary>
/// <param name="data"></param>
public NameValue(string data)
{
int i;
// Name
i = data.IndexOfAny(Separators);
if (i < 1)
{
Name = String.Empty;
Type = ValueType.Unknown;
Class = ClassType.Unknown;
Sendto = SendtoType.Unknown;
Value = null;
return;
}
Name = data.Substring(0, i);
data = data.Substring(i + 1);
// Type
i = data.IndexOfAny(Separators);
if (i > 0)
{
Type = GetValueType(data.Substring(0, i));
data = data.Substring(i + 1);
// Class
i = data.IndexOfAny(Separators);
if (i > 0)
{
Class = GetClassType(data.Substring(0, i));
data = data.Substring(i + 1);
// Sendto
i = data.IndexOfAny(Separators);
if (i > 0)
{
Sendto = GetSendtoType(data.Substring(0, 1));
data = data.Substring(i + 1);
}
}
}
// Value
Type = ValueType.String;
Class = ClassType.ReadOnly;
Sendto = SendtoType.Sim;
Value = null;
SetValue(data);
}
public static string NameValuesToString(NameValue[] values)
{
if (values == null || values.Length == 0)
return String.Empty;
StringBuilder output = new StringBuilder();
for (int i = 0; i < values.Length; i++)
{
NameValue value = values[i];
if (value.Value != null)
{
string newLine = (i < values.Length - 1) ? "\n" : String.Empty;
output.AppendFormat("{0} {1} {2} {3} {4}{5}", value.Name, TypeStrings[(int)value.Type],
ClassStrings[(int)value.Class], SendtoStrings[(int)value.Sendto], value.Value.ToString(), newLine);
}
}
return output.ToString();
}
private void SetValue(string value)
{
switch (Type)
{
case ValueType.Asset:
case ValueType.String:
Value = value;
break;
case ValueType.F32:
{
float temp;
Utils.TryParseSingle(value, out temp);
Value = temp;
break;
}
case ValueType.S32:
{
int temp;
Int32.TryParse(value, out temp);
Value = temp;
break;
}
case ValueType.U32:
{
uint temp;
UInt32.TryParse(value, out temp);
Value = temp;
break;
}
case ValueType.U64:
{
ulong temp;
UInt64.TryParse(value, out temp);
Value = temp;
break;
}
case ValueType.VEC3:
{
Vector3 temp;
Vector3.TryParse(value, out temp);
Value = temp;
break;
}
default:
Value = null;
break;
}
}
private static ValueType GetValueType(string value)
{
ValueType type = ValueType.Unknown;
for (int i = 0; i < TypeStrings.Length; i++)
{
if (value == TypeStrings[i])
{
type = (ValueType)i;
break;
}
}
if (type == ValueType.Unknown)
type = ValueType.String;
return type;
}
private static ClassType GetClassType(string value)
{
ClassType type = ClassType.Unknown;
for (int i = 0; i < ClassStrings.Length; i++)
{
if (value == ClassStrings[i])
{
type = (ClassType)i;
break;
}
}
if (type == ClassType.Unknown)
type = ClassType.ReadOnly;
return type;
}
private static SendtoType GetSendtoType(string value)
{
SendtoType type = SendtoType.Unknown;
for (int i = 0; i < SendtoStrings.Length; i++)
{
if (value == SendtoStrings[i])
{
type = (SendtoType)i;
break;
}
}
if (type == SendtoType.Unknown)
type = SendtoType.Sim;
return type;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Net;
namespace OpenMetaverse
{
// this class encapsulates a single packet that
// is either sent or received by a UDP socket
public class UDPPacketBuffer
{
/// <summary>Size of the byte array used to store raw packet data</summary>
public const int BUFFER_SIZE = 4096;
/// <summary>Raw packet data buffer</summary>
public readonly byte[] Data;
/// <summary>Length of the data to transmit</summary>
public int DataLength;
/// <summary>EndPoint of the remote host</summary>
public EndPoint RemoteEndPoint;
/// <summary>
/// Create an allocated UDP packet buffer for receiving a packet
/// </summary>
public UDPPacketBuffer()
{
Data = new byte[UDPPacketBuffer.BUFFER_SIZE];
// Will be modified later by BeginReceiveFrom()
RemoteEndPoint = new IPEndPoint(Settings.BIND_ADDR, 0);
}
/// <summary>
/// Create an allocated UDP packet buffer for sending a packet
/// </summary>
/// <param name="endPoint">EndPoint of the remote host</param>
public UDPPacketBuffer(IPEndPoint endPoint)
{
Data = new byte[UDPPacketBuffer.BUFFER_SIZE];
RemoteEndPoint = endPoint;
}
/// <summary>
/// Create an allocated UDP packet buffer for sending a packet
/// </summary>
/// <param name="endPoint">EndPoint of the remote host</param>
/// <param name="bufferSize">Size of the buffer to allocate for packet data</param>
public UDPPacketBuffer(IPEndPoint endPoint, int bufferSize)
{
Data = new byte[bufferSize];
RemoteEndPoint = endPoint;
}
}
/// <summary>
/// Object pool for packet buffers. This is used to allocate memory for all
/// incoming and outgoing packets, and zerocoding buffers for those packets
/// </summary>
public class PacketBufferPool : ObjectPoolBase<UDPPacketBuffer>
{
private IPEndPoint EndPoint;
/// <summary>
/// Initialize the object pool in client mode
/// </summary>
/// <param name="endPoint">Server to connect to</param>
/// <param name="itemsPerSegment"></param>
/// <param name="minSegments"></param>
public PacketBufferPool(IPEndPoint endPoint, int itemsPerSegment, int minSegments)
: base()
{
EndPoint = endPoint;
Initialize(itemsPerSegment, minSegments, true, 1000 * 60 * 5);
}
/// <summary>
/// Initialize the object pool in server mode
/// </summary>
/// <param name="itemsPerSegment"></param>
/// <param name="minSegments"></param>
public PacketBufferPool(int itemsPerSegment, int minSegments)
: base()
{
EndPoint = null;
Initialize(itemsPerSegment, minSegments, true, 1000 * 60 * 5);
}
/// <summary>
/// Returns a packet buffer with EndPoint set if the buffer is in
/// client mode, or with EndPoint set to null in server mode
/// </summary>
/// <returns>Initialized UDPPacketBuffer object</returns>
protected override UDPPacketBuffer GetObjectInstance()
{
if (EndPoint != null)
// Client mode
return new UDPPacketBuffer(EndPoint);
else
// Server mode
return new UDPPacketBuffer();
}
}
public static class Pool
{
public static PacketBufferPool PoolInstance;
/// <summary>
/// Default constructor
/// </summary>
static Pool()
{
PoolInstance = new PacketBufferPool(new IPEndPoint(Settings.BIND_ADDR, 0), 16, 1);
}
/// <summary>
/// Check a packet buffer out of the pool
/// </summary>
/// <returns>A packet buffer object</returns>
public static WrappedObject<UDPPacketBuffer> CheckOut()
{
return PoolInstance.CheckOut();
}
}
}
+529
View File
@@ -0,0 +1,529 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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;
using System.Collections.Generic;
using System.Threading;
namespace OpenMetaverse
{
public sealed class WrappedObject<T> : IDisposable where T : class
{
private T _instance;
internal readonly ObjectPoolSegment<T> _owningSegment;
internal readonly ObjectPoolBase<T> _owningObjectPool;
private bool _disposed = false;
internal WrappedObject(ObjectPoolBase<T> owningPool, ObjectPoolSegment<T> ownerSegment, T activeInstance)
{
_owningObjectPool = owningPool;
_owningSegment = ownerSegment;
_instance = activeInstance;
}
~WrappedObject()
{
#if !PocketPC
// If the AppDomain is being unloaded, or the CLR is
// shutting down, just exit gracefully
if (Environment.HasShutdownStarted)
return;
#endif
// Object Resurrection in Action!
GC.ReRegisterForFinalize(this);
// Return this instance back to the owning queue
_owningObjectPool.CheckIn(_owningSegment, _instance);
}
/// <summary>
/// Returns an instance of the class that has been checked out of the Object Pool.
/// </summary>
public T Instance
{
get
{
if (_disposed)
throw new ObjectDisposedException("WrappedObject");
return _instance;
}
}
/// <summary>
/// Checks the instance back into the object pool
/// </summary>
public void Dispose()
{
if (_disposed)
return;
_disposed = true;
_owningObjectPool.CheckIn(_owningSegment, _instance);
GC.SuppressFinalize(this);
}
}
public abstract class ObjectPoolBase<T> : IDisposable where T : class
{
private int _itemsPerSegment = 32;
private int _minimumSegmentCount = 1;
// A segment won't be eligible for cleanup unless it's at least this old...
private TimeSpan _minimumAgeToCleanup = new TimeSpan(0, 5, 0);
// ever increasing segment counter
private int _activeSegment = 0;
private bool _gc = true;
private volatile bool _disposed = false;
private Dictionary<int, ObjectPoolSegment<T>> _segments = new Dictionary<int, ObjectPoolSegment<T>>();
private object _syncRoot = new object();
private object _timerLock = new object();
// create a timer that starts in 5 minutes, and gets called every 5 minutes.
System.Threading.Timer _timer;
int _cleanupFrequency;
/// <summary>
/// Creates a new instance of the ObjectPoolBase class. Initialize MUST be called
/// after using this constructor.
/// </summary>
protected ObjectPoolBase()
{
}
/// <summary>
/// Creates a new instance of the ObjectPool Base class.
/// </summary>
/// <param name="itemsPerSegment">The object pool is composed of segments, which
/// are allocated whenever the size of the pool is exceeded. The number of items
/// in a segment should be large enough that allocating a new segmeng is a rare
/// thing. For example, on a server that will have 10k people logged in at once,
/// the receive buffer object pool should have segment sizes of at least 1000
/// byte arrays per segment.
/// </param>
/// <param name="minimumSegmentCount">The minimun number of segments that may exist.</param>
/// <param name="gcOnPoolGrowth">Perform a full GC.Collect whenever a segment is allocated, and then again after allocation to compact the heap.</param>
/// <param name="cleanupFrequenceMS">The frequency which segments are checked to see if they're eligible for cleanup.</param>
protected ObjectPoolBase(int itemsPerSegment, int minimumSegmentCount, bool gcOnPoolGrowth, int cleanupFrequenceMS)
{
Initialize(itemsPerSegment, minimumSegmentCount, gcOnPoolGrowth, cleanupFrequenceMS);
}
protected void Initialize(int itemsPerSegment, int minimumSegmentCount, bool gcOnPoolGrowth, int cleanupFrequenceMS)
{
_itemsPerSegment = itemsPerSegment;
_minimumSegmentCount = minimumSegmentCount;
_gc = gcOnPoolGrowth;
// force garbage collection to make sure these new long lived objects
// cause as little fragmentation as possible
if (_gc)
System.GC.Collect();
lock (_syncRoot)
{
while (_segments.Count < this.MinimumSegmentCount)
{
ObjectPoolSegment<T> segment = CreateSegment(false);
_segments.Add(segment.SegmentNumber, segment);
}
}
// This forces a compact, to make sure our objects fill in any holes in the heap.
if (_gc)
{
System.GC.Collect();
}
_timer = new Timer(CleanupThreadCallback, null, cleanupFrequenceMS, cleanupFrequenceMS);
}
/// <summary>
/// Forces the segment cleanup algorithm to be run. This method is intended
/// primarly for use from the Unit Test libraries.
/// </summary>
internal void ForceCleanup()
{
CleanupThreadCallback(null);
}
private void CleanupThreadCallback(object state)
{
if (_disposed)
return;
if (Monitor.TryEnter(_timerLock) == false)
return;
try
{
lock (_syncRoot)
{
// If we're below, or at, or minimum segment count threshold,
// there's no point in going any further.
if (_segments.Count <= _minimumSegmentCount)
return;
for (int i = _activeSegment; i > 0; i--)
{
ObjectPoolSegment<T> segment;
if (_segments.TryGetValue(i, out segment) == true)
{
// For the "old" segments that were allocated at startup, this will
// always be false, as their expiration dates are set at infinity.
if (segment.CanBeCleanedUp())
{
_segments.Remove(i);
segment.Dispose();
}
}
}
}
}
finally
{
Monitor.Exit(_timerLock);
}
}
/// <summary>
/// Responsible for allocate 1 instance of an object that will be stored in a segment.
/// </summary>
/// <returns>An instance of whatever objec the pool is pooling.</returns>
protected abstract T GetObjectInstance();
private ObjectPoolSegment<T> CreateSegment(bool allowSegmentToBeCleanedUp)
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
if (allowSegmentToBeCleanedUp)
Logger.Log("Creating new object pool segment", Helpers.LogLevel.Info);
// This method is called inside a lock, so no interlocked stuff required.
int segmentToAdd = _activeSegment;
_activeSegment++;
Queue<T> buffers = new Queue<T>();
for (int i = 1; i <= this._itemsPerSegment; i++)
{
T obj = GetObjectInstance();
buffers.Enqueue(obj);
}
// certain segments we don't want to ever be cleaned up (the initial segments)
DateTime cleanupTime = (allowSegmentToBeCleanedUp) ? DateTime.Now.Add(this._minimumAgeToCleanup) : DateTime.MaxValue;
ObjectPoolSegment<T> segment = new ObjectPoolSegment<T>(segmentToAdd, buffers, cleanupTime);
return segment;
}
/// <summary>
/// Checks in an instance of T owned by the object pool. This method is only intended to be called
/// by the <c>WrappedObject</c> class.
/// </summary>
/// <param name="owningSegment">The segment from which the instance is checked out.</param>
/// <param name="instance">The instance of <c>T</c> to check back into the segment.</param>
internal void CheckIn(ObjectPoolSegment<T> owningSegment, T instance)
{
lock (_syncRoot)
{
owningSegment.CheckInObject(instance);
}
}
/// <summary>
/// Checks an instance of <c>T</c> from the pool. If the pool is not sufficient to
/// allow the checkout, a new segment is created.
/// </summary>
/// <returns>A <c>WrappedObject</c> around the instance of <c>T</c>. To check
/// the instance back into the segment, be sureto dispose the WrappedObject
/// when finished. </returns>
public WrappedObject<T> CheckOut()
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
// It's key that this CheckOut always, always, uses a pooled object
// from the oldest available segment. This will help keep the "newer"
// segments from being used - which in turn, makes them eligible
// for deletion.
lock (_syncRoot)
{
ObjectPoolSegment<T> targetSegment = null;
// find the oldest segment that has items available for checkout
for (int i = 0; i < _activeSegment; i++)
{
ObjectPoolSegment<T> segment;
if (_segments.TryGetValue(i, out segment) == true)
{
if (segment.AvailableItems > 0)
{
targetSegment = segment;
break;
}
}
}
if (targetSegment == null)
{
// We couldn't find a sigment that had any available space in it,
// so it's time to create a new segment.
// Before creating the segment, do a GC to make sure the heap
// is compacted.
if (_gc) GC.Collect();
targetSegment = CreateSegment(true);
if (_gc) GC.Collect();
_segments.Add(targetSegment.SegmentNumber, targetSegment);
}
WrappedObject<T> obj = new WrappedObject<T>(this, targetSegment, targetSegment.CheckOutObject());
return obj;
}
}
/// <summary>
/// The total number of segments created. Intended to be used by the Unit Tests.
/// </summary>
public int TotalSegments
{
get
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
lock (_syncRoot)
{
return _segments.Count;
}
}
}
/// <summary>
/// The number of items that are in a segment. Items in a segment
/// are all allocated at the same time, and are hopefully close to
/// each other in the managed heap.
/// </summary>
public int ItemsPerSegment
{
get
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
return _itemsPerSegment;
}
}
/// <summary>
/// The minimum number of segments. When segments are reclaimed,
/// this number of segments will always be left alone. These
/// segments are allocated at startup.
/// </summary>
public int MinimumSegmentCount
{
get
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
return _minimumSegmentCount;
}
}
/// <summary>
/// The age a segment must be before it's eligible for cleanup.
/// This is used to prevent thrash, and typical values are in
/// the 5 minute range.
/// </summary>
public TimeSpan MinimumSegmentAgePriorToCleanup
{
get
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
return _minimumAgeToCleanup;
}
set
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
_minimumAgeToCleanup = value;
}
}
/// <summary>
/// The frequence which the cleanup thread runs. This is typically
/// expected to be in the 5 minute range.
/// </summary>
public int CleanupFrequencyMilliseconds
{
get
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
return _cleanupFrequency;
}
set
{
if (_disposed)
throw new ObjectDisposedException("ObjectPoolBase");
Interlocked.Exchange(ref _cleanupFrequency, value);
_timer.Change(_cleanupFrequency, _cleanupFrequency);
}
}
#region IDisposable Members
public void Dispose()
{
if (_disposed)
return;
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (_syncRoot)
{
if (_disposed)
return;
_timer.Dispose();
_disposed = true;
foreach (KeyValuePair<int, ObjectPoolSegment<T>> kvp in _segments)
{
try
{
kvp.Value.Dispose();
}
catch (Exception) { }
}
_segments.Clear();
}
}
}
#endregion
}
internal class ObjectPoolSegment<T> : IDisposable where T : class
{
private Queue<T> _liveInstances = new Queue<T>();
private int _segmentNumber;
private int _originalCount;
private bool _isDisposed = false;
private DateTime _eligibleForDeletionAt;
public int SegmentNumber { get { return _segmentNumber; } }
public int AvailableItems { get { return _liveInstances.Count; } }
public DateTime DateEligibleForDeletion { get { return _eligibleForDeletionAt; } }
public ObjectPoolSegment(int segmentNumber, Queue<T> liveInstances, DateTime eligibleForDeletionAt)
{
_segmentNumber = segmentNumber;
_liveInstances = liveInstances;
_originalCount = liveInstances.Count;
_eligibleForDeletionAt = eligibleForDeletionAt;
}
public bool CanBeCleanedUp()
{
if (_isDisposed == true)
throw new ObjectDisposedException("ObjectPoolSegment");
return ((_originalCount == _liveInstances.Count) && (DateTime.Now > _eligibleForDeletionAt));
}
public void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
bool shouldDispose = (typeof(T) is IDisposable);
while (_liveInstances.Count != 0)
{
T instance = _liveInstances.Dequeue();
if (shouldDispose)
{
try
{
(instance as IDisposable).Dispose();
}
catch (Exception) { }
}
}
}
internal void CheckInObject(T o)
{
if (_isDisposed == true)
throw new ObjectDisposedException("ObjectPoolSegment");
_liveInstances.Enqueue(o);
}
internal T CheckOutObject()
{
if (_isDisposed == true)
throw new ObjectDisposedException("ObjectPoolSegment");
if (0 == _liveInstances.Count)
throw new InvalidOperationException("No Objects Available for Checkout");
T o = _liveInstances.Dequeue();
return o;
}
}
}
+337
View File
@@ -0,0 +1,337 @@
/*
* Copyright (c) 2006-2014, openmetaverse.org
* 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 openmetaverse.org 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.Collections;
namespace OpenMetaverse
{
/// <summary>
///
/// </summary>
public enum DictionaryEventAction
{
/// <summary>
///
/// </summary>
Add,
/// <summary>
///
/// </summary>
Remove,
/// <summary>
///
/// </summary>
Change
}
/// <summary>
///
/// </summary>
/// <param name="action"></param>
/// <param name="entry"></param>
public delegate void DictionaryChangeCallback(DictionaryEventAction action, DictionaryEntry entry);
/// <summary>
/// The ObservableDictionary class is used for storing key/value pairs. It has methods for firing
/// events to subscribers when items are added, removed, or changed.
/// </summary>
/// <typeparam name="TKey">Key <see langword="Tkey"/></typeparam>
/// <typeparam name="TValue">Value <see langword="TValue"/></typeparam>
public class ObservableDictionary<TKey, TValue>
{
#region Observable implementation
/// <summary>
/// A dictionary of callbacks to fire when specified action occurs
/// </summary>
private Dictionary<DictionaryEventAction, List<DictionaryChangeCallback>> Delegates;
/// <summary>
/// Register a callback to be fired when an action occurs
/// </summary>
/// <param name="action">The action</param>
/// <param name="callback">The callback to fire</param>
public void AddDelegate(DictionaryEventAction action, DictionaryChangeCallback callback)
{
if (Delegates.ContainsKey(action))
{
Delegates[action].Add(callback);
}
else
{
List<DictionaryChangeCallback> callbacks = new List<DictionaryChangeCallback>(1);
callbacks.Add(callback);
Delegates.Add(action, callbacks);
}
}
/// <summary>
/// Unregister a callback
/// </summary>
/// <param name="action">The action</param>
/// <param name="callback">The callback to fire</param>
public void RemoveDelegate(DictionaryEventAction action, DictionaryChangeCallback callback)
{
if (Delegates.ContainsKey(action))
{
if (Delegates[action].Contains(callback))
Delegates[action].Remove(callback);
}
}
/// <summary>
///
/// </summary>
/// <param name="action"></param>
/// <param name="entry"></param>
private void FireChangeEvent(DictionaryEventAction action, DictionaryEntry entry)
{
if(Delegates.ContainsKey(action))
{
foreach(DictionaryChangeCallback handler in Delegates[action])
{
handler(action, entry);
}
}
}
#endregion
/// <summary>Internal dictionary that this class wraps around. Do not
/// modify or enumerate the contents of this dictionary without locking</summary>
private Dictionary<TKey, TValue> Dictionary;
/// <summary>
/// Gets the number of Key/Value pairs contained in the <seealso cref="T:ObservableDictionary"/>
/// </summary>
public int Count { get { return Dictionary.Count; } }
/// <summary>
/// Initializes a new instance of the <seealso cref="T:ObservableDictionary"/> Class
/// with the specified key/value, has the default initial capacity.
/// </summary>
/// <example>
/// <code>
/// // initialize a new ObservableDictionary named testDict with a string as the key and an int as the value.
/// public ObservableDictionary&lt;string, int&gt; testDict = new ObservableDictionary&lt;string, int&gt;();
/// </code>
/// </example>
public ObservableDictionary()
{
Dictionary = new Dictionary<TKey, TValue>();
Delegates = new Dictionary<DictionaryEventAction, List<DictionaryChangeCallback>>();
}
/// <summary>
/// Initializes a new instance of the <seealso cref="T:OpenMetaverse.ObservableDictionary"/> Class
/// with the specified key/value, With its initial capacity specified.
/// </summary>
/// <param name="capacity">Initial size of dictionary</param>
/// <example>
/// <code>
/// // initialize a new ObservableDictionary named testDict with a string as the key and an int as the value,
/// // initially allocated room for 10 entries.
/// public ObservableDictionary&lt;string, int&gt; testDict = new ObservableDictionary&lt;string, int&gt;(10);
/// </code>
/// </example>
public ObservableDictionary(int capacity)
{
Dictionary = new Dictionary<TKey, TValue>(capacity);
Delegates = new Dictionary<DictionaryEventAction, List<DictionaryChangeCallback>>();
}
/// <summary>
/// Try to get entry from the <seealso cref="ObservableDictionary"/> with specified key
/// </summary>
/// <param name="key">Key to use for lookup</param>
/// <param name="value">Value returned</param>
/// <returns><see langword="true"/> if specified key exists, <see langword="false"/> if not found</returns>
/// <example>
/// <code>
/// // find your avatar using the Simulator.ObjectsAvatars ObservableDictionary:
/// Avatar av;
/// if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(Client.Self.AgentID, out av))
/// Console.WriteLine("Found Avatar {0}", av.Name);
/// </code>
/// <seealso cref="Simulator.ObjectsAvatars"/>
/// </example>
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
/// <summary>
/// Finds the specified match.
/// </summary>
/// <param name="match">The match.</param>
/// <returns>Matched value</returns>
/// <example>
/// <code>
/// // use a delegate to find a prim in the ObjectsPrimitives ObservableDictionary
/// // with the ID 95683496
/// uint findID = 95683496;
/// Primitive findPrim = sim.ObjectsPrimitives.Find(
/// delegate(Primitive prim) { return prim.ID == findID; });
/// </code>
/// </example>
public TValue Find(Predicate<TValue> match)
{
foreach (TValue value in Dictionary.Values)
{
if (match(value))
return value;
}
return default(TValue);
}
/// <summary>Find All items in an <seealso cref="T:ObservableDictionary"/></summary>
/// <param name="match">return matching items.</param>
/// <returns>a <seealso cref="T:System.Collections.Generic.List"/> containing found items.</returns>
/// <example>
/// Find All prims within 20 meters and store them in a List
/// <code>
/// int radius = 20;
/// List&lt;Primitive&gt; prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(
/// delegate(Primitive prim) {
/// Vector3 pos = prim.Position;
/// return ((prim.ParentID == 0) &amp;&amp; (pos != Vector3.Zero) &amp;&amp; (Vector3.Distance(pos, location) &lt; radius));
/// }
/// );
///</code>
///</example>
public List<TValue> FindAll(Predicate<TValue> match)
{
List<TValue> found = new List<TValue>();
foreach (KeyValuePair<TKey, TValue> kvp in Dictionary)
{
if (match(kvp.Value))
found.Add(kvp.Value);
}
return found;
}
/// <summary>Find All items in an <seealso cref="T:ObservableDictionary"/></summary>
/// <param name="match">return matching keys.</param>
/// <returns>a <seealso cref="T:System.Collections.Generic.List"/> containing found keys.</returns>
/// <example>
/// Find All keys which also exist in another dictionary
/// <code>
/// List&lt;UUID&gt; matches = myDict.FindAll(
/// delegate(UUID id) {
/// return myOtherDict.ContainsKey(id);
/// }
/// );
///</code>
///</example>
public List<TKey> FindAll(Predicate<TKey> match)
{
List<TKey> found = new List<TKey>();
foreach (KeyValuePair<TKey, TValue> kvp in Dictionary)
{
if (match(kvp.Key))
found.Add(kvp.Key);
}
return found;
}
/// <summary>Check if Key exists in Dictionary</summary>
/// <param name="key">Key to check for</param>
/// <returns><see langword="true"/> if found, <see langword="false"/> otherwise</returns>
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
/// <summary>Check if Value exists in Dictionary</summary>
/// <param name="value">Value to check for</param>
/// <returns><see langword="true"/> if found, <see langword="false"/> otherwise</returns>
public bool ContainsValue(TValue value)
{
return Dictionary.ContainsValue(value);
}
/// <summary>
/// Adds the specified key to the dictionary, dictionary locking is not performed,
/// <see cref="SafeAdd"/>
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
public void Add(TKey key, TValue value)
{
Dictionary.Add(key, value);
FireChangeEvent(DictionaryEventAction.Add, new DictionaryEntry(key, value));
}
/// <summary>
/// Removes the specified key, dictionary locking is not performed
/// </summary>
/// <param name="key">The key.</param>
/// <returns><see langword="true"/> if successful, <see langword="false"/> otherwise</returns>
public bool Remove(TKey key)
{
FireChangeEvent(DictionaryEventAction.Remove, new DictionaryEntry(key, Dictionary[key]));
return Dictionary.Remove(key);
}
/// <summary>
/// Indexer for the dictionary
/// </summary>
/// <param name="key">The key</param>
/// <returns>The value</returns>
public TValue this[TKey key]
{
get { return Dictionary[key]; }
set { FireChangeEvent(DictionaryEventAction.Add, new DictionaryEntry(key, value));
Dictionary[key] = value; }
}
/// <summary>
/// Clear the contents of the dictionary
/// </summary>
public void Clear()
{
foreach (KeyValuePair<TKey, TValue> kvp in Dictionary)
FireChangeEvent(DictionaryEventAction.Remove, new DictionaryEntry(kvp.Key, kvp.Value));
Dictionary.Clear();
}
/// <summary>
/// Enumerator for iterating dictionary entries
/// </summary>
/// <returns></returns>
public System.Collections.IEnumerator GetEnumerator()
{
return Dictionary.GetEnumerator();
}
}
}

Some files were not shown because too many files have changed in this diff Show More