// // ORIGINAL CODE BASE IS Copyright (C) 2006-2010 by Alphons van der Heijden. // The code was donated on 2010-04-28 by Alphons van der Heijden to Brandon 'Dimentox Travanti' Husbands & // Malcolm J. Kudra, who in turn License under the GPLv2 in agreement with Alphons van der Heijden's wishes. // // The community would like to thank Alphons for all of his hard work, blood sweat and tears. Without his work // the community would be stuck with crappy editors. // // The source code in this file ("Source Code") is provided by The LSLEditor Group to you under the terms of the GNU // General Public License, version 2.0 ("GPL"), unless you have obtained a separate licensing agreement ("Other // License"), formally executed by you and The LSLEditor Group. // Terms of the GPL can be found in the gplv2.txt document. // // GPLv2 Header // ************ // LSLEditor, a External editor for the LSL Language. // Copyright (C) 2010 The LSLEditor Group. // // This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any // later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with this program; if not, write to the Free // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // ******************************************************************************************************************** // The above copyright notice and this permission notice shall be included in copies or substantial portions of the // Software. // ******************************************************************************************************************** // // // // SecondLifeMain.cs // // using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; /* // (C) 2006,2007 Alphons van der Heijden // mail: alphons@heijden.com */ [module: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] [module: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText", Justification = "Reviewed.")] namespace LSLEditor { /// /// Enumeration of Communication Types that the host understands. /// public enum CommunicationType { /// /// Indicates the message can be heard only within 5m. /// Whisper, /// /// Indicates the message can be heard only within 20m. /// Say, /// /// Indicates the message can be heard only within 100m. /// Shout, /// /// Indicates the message can be only heard by the owner any where within the same simulator. /// OwnerSay, /// /// Indicates the message can be heard any where within the same simulator. /// RegionSay, /// /// Indicates the message can be heard any where within the same simulator, by a specific object. /// RegionSayTo } /// /// Partial definition of SecondLife class to handle running scripts. /// public partial class SecondLife { /// /// Holds the host object. /// private SecondLifeHost slhHost; #region members /// /// Random generator. /// private Random rdmRandom; /// /// Holds the time of the script starting execution. /// private DateTime dtDateTimeScriptStarted; /// /// Contains a boolean value indicating whether this object accepts other items to be dropped into it. /// private bool blnAllowDrop = false; /// /// Contains a list of keys of avatars that may enter a parcel. /// private Hashtable htLandPassList; /// /// Contains a list of keys of avatars that may NOT enter a parcel. /// private Hashtable htLandBanList; /// /// Volume of sound played by this prim. /// private Float fVolume; /// /// Name of the object/prim. /// private String sObjectName; /// /// URL for parcel's music stream. /// private String sParcelMusicURL; /// /// Position of object/prim placement in a simulator. /// private vector vPosition; /// /// Rotation of the object/prim's placement in the simulator. /// private rotation rRotation; /// /// Local rotation of the prim with respect to the object root. /// private rotation rRotationlocal; /// /// Scale of the object/prim. /// private vector vScale; /// /// Text for the "Sit" entry on object menu. /// private String sSitText; /// /// Radius that sound may be heard around the object/prim. /// private Float fSoundRadius; /// /// Region Coordinates of the simulator's bottom-left corner. /// private vector vRegionCorner; /// /// Parameter passed to the script on start up. /// private integer iStartParameter; #endregion #region Constructor /// /// Initialises a new instance of the class. /// public SecondLife() { this.host = null; rdmRandom = new Random(); dtDateTimeScriptStarted = DateTime.Now.ToUniversalTime(); htLandPassList = new Hashtable(); htLandBanList = new Hashtable(); fVolume = 0.0; sObjectName = null; vPosition = new vector(127, 128, 20); rRotation = rotation.ZERO_ROTATION; rRotationlocal = rotation.ZERO_ROTATION; vScale = vector.ZERO_VECTOR; sSitText = "sittext"; fSoundRadius = 1.0; iStartParameter = 0; vRegionCorner = vector.ZERO_VECTOR; } #endregion #region Properties /// /// Gets the position of the object within the simulator. /// /// Gets the vPosition data member. public vector GetLocalPos { // TODO change this to use a call to llGetLocalPos specifying NOT to be Verbose. After all functions have been changed to allow verbose/silent option. get { return vPosition; } } /// /// Gets or sets the SecondLifeHost object. /// /// The host property gets/sets the slhHost data member. public SecondLifeHost host { get { return this.slhHost; } set { this.slhHost = value; } } #endregion #region internal routines /// /// Outputs messages during execution of the script. /// /// The text (with possible placeholders) to be output. /// Values to be put into corresponding placeholders. private void Verbose(string strLine, params object[] parameters) { if (parameters.Length == 0) { host.VerboseMessage(strLine); } else { this.host.VerboseMessage(string.Format(strLine, parameters)); } } /// /// Wrapper to call the SecondLifeHost.Chat method. /// /// Channel for message to be sent on. /// Text of message to send. /// Type of communication (from CommunicationType enumerator). private void Chat(integer iChannel, string sText, CommunicationType ctHow) { this.host.Chat(this.host, iChannel, this.host.GetObjectName(), this.host.GetKey(), sText, ctHow); } /// /// Method to trigger change in script state. /// /// Name of state to switch to. public void state(string strStateName) { Verbose("state->" + strStateName); host.State(strStateName, false); System.Threading.Thread.Sleep(1000); System.Windows.Forms.MessageBox.Show("If you see this, something is wrong", "Oops..."); } #endregion #region Helper Functions #region List Functions /// /// Takes the possibly negative value list indexes and corrects them to be absolute indexes. /// /// Length of list. /// Start index. /// End index. /// True, unless an error occurred, then false. private bool CorrectIt(int intLength, ref int intStart, ref int intEnd) { bool bResult = true; if (intStart < 0) { intStart = intLength + intStart; } if (intEnd < 0) { intEnd = intLength + intEnd; } if (intStart <= intEnd) { if (intStart >= intLength) { bResult = false; } if (intEnd < 0) { bResult = false; } } intStart = Math.Max(0, intStart); intEnd = Math.Min(intLength - 1, intEnd); return bResult; } /// /// Randomly rearranges the list of items. /// /// List to shuffle. /// The reordered list. public ArrayList RandomShuffle(ArrayList alCollection) { /* We have to copy all items anyway, and there isn't a way to produce the items on the fly that is linear. So copying to an array and shuffling it is as efficient as we can get. */ if (alCollection == null) { throw new ArgumentNullException("collection"); } int intCount = alCollection.Count; for (int i = intCount - 1; i >= 1; --i) { // Pick an random number 0 through i inclusive. int j = rdmRandom.Next(i + 1); // Swap array[i] and array[j] object temp = alCollection[i]; alCollection[i] = alCollection[j]; alCollection[j] = temp; } return alCollection; } /// /// Convert a list into an array of buckets containing Stride elements. /// /// List of items. /// Number of element for each bucket. /// The list separated into elements of Stride length. private ArrayList List2Buckets(list lSource, int intStride) { ArrayList alBuckets = null; if ((lSource.Count % intStride) == 0 && intStride <= lSource.Count) { alBuckets = new ArrayList(); for (int intI = 0; intI < lSource.Count; intI += intStride) { object[] bucket = new object[intStride]; for (int intJ = 0; intJ < intStride; intJ++) { bucket[intJ] = lSource[intI + intJ]; } alBuckets.Add(bucket); } } return alBuckets; } /// /// Converts buckets back into a list. /// /// Array of buckets. /// Stride value. /// The items recombined into a simple list. private list Buckets2List(ArrayList alBuckets, int intStride) { object[] items = new object[alBuckets.Count * intStride]; for (int intI = 0; intI < alBuckets.Count; intI++) { for (int intJ = 0; intJ < intStride; intJ++) { items[(intI * intStride) + intJ] = ((object[])alBuckets[intI])[intJ]; } } return new list(items); } /// /// Implements the Comparer Interface for our custom types (Float, Integer, and String). /// private class BucketComparer : IComparer { /// /// True/false parameter indicating whether the comparison should be done in ascending or descending order. /// private integer iAscending; /// /// Initialises a new instance of the class. /// /// [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText", Justification = "Reviewed.")] public BucketComparer(integer ascending) { this.iAscending = ascending; } /// /// Perform the comparison between two objects. /// /// First object. /// Second object. /// An integer (-1, 0, or 1), indicating whether the first item is less than, same as, or greater than the second. public int Compare(object x, object y) { int iResult = 0; object objA, objB; object[] xx = x as object[]; object[] yy = y as object[]; if (iAscending == TRUE) { objA = xx[0]; objB = yy[0]; } else { objB = xx[0]; objA = yy[0]; } string strType = objA.GetType().ToString(); if (((objA is string) && (objB is string)) || ((objA is SecondLife.String) && (objB is SecondLife.String))) { iResult = string.Compare(objA.ToString(), objB.ToString()); } else if ((objA is SecondLife.integer) && (objB is SecondLife.integer)) { iResult = SecondLife.integer.Compare((SecondLife.integer)objA, (SecondLife.integer)objB); } else if ((objA is SecondLife.Float) && (objB is SecondLife.Float)) { iResult = SecondLife.Float.Compare((SecondLife.Float)objA, (SecondLife.Float)objB); } return iResult; } } #endregion #region String Functions /// /// Takes a string and splits it into a list based upon separators (which are not kept) and spacers (which are). /// /// The source text. /// Separators to split on. /// Spacers to split on. /// True/false indicating whether nulls are kept in the resulting list. /// A new list of the split string. private list ParseString(String sSource, list lSeparators, list lSpacers, bool blnKeepNulls) { list lResult = new list(); int intFrom = 0; string s = sSource; for (int intI = 0; intI < s.Length; intI++) { string strTmp = s.Substring(intI); bool blnFound = false; for (int intJ = 0; intJ < lSeparators.Count; intJ++) { string strSeparator = lSeparators[intJ].ToString(); if (strSeparator.Length == 0) { continue; // check this } if (strTmp.IndexOf(strSeparator) == 0) { string strBefore = s.Substring(intFrom, intI - intFrom); if (strBefore != "" || blnKeepNulls) { lResult.Add(strBefore); } intI += strSeparator.Length - 1; intFrom = intI + 1; blnFound = true; break; } } if (blnFound) { continue; } for (int intJ = 0; intJ < lSpacers.Count; intJ++) { string strSpacer = lSpacers[intJ].ToString(); if (strSpacer.Length == 0) { continue; // check this } if (strTmp.IndexOf(strSpacer) == 0) { string strBefore = s.Substring(intFrom, intI - intFrom); if (strBefore != "" || blnKeepNulls) { lResult.Add(strBefore); } lResult.Add(strSpacer); intI += strSpacer.Length - 1; intFrom = intI + 1; break; } } } string strLast = s.Substring(intFrom); if (strLast != "" || blnKeepNulls) { lResult.Add(strLast); } return lResult; } /// /// Convert string to Base64 representation. /// /// Source string. /// Base64 encoding of the source string. private string StringToBase64(string strText) { byte[] data = Encoding.UTF8.GetBytes(strText); return Convert.ToBase64String(data); } /// /// Converts Base64 encoded string back to UTF-8. /// /// Base64 encoded string. /// UTF-8 string. private string Base64ToString(string strText) { byte[] data = Convert.FromBase64String(strText); int intLen = Array.IndexOf(data, (byte)0x00); if (intLen < 0) { intLen = data.Length; } return Encoding.UTF8.GetString(data, 0, intLen); } /// /// Performs the lookup. /// /// String source. /// Byte within string. /// The Base64 value of the element. private int LookupBase64(string s, int intIndex) { int intReturn = 0; if (intIndex < s.Length) { intReturn = FastLookupBase64[s[intIndex]]; if (intReturn == 0) { if (s[intIndex] != 'A') { throw new Exception(); } } } else { intReturn = 0; } return intReturn; } /// /// Pre-generated matrix for quicker lookup. /// ////[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "Stays with other string functions.")] private static readonly int[] FastLookupBase64 = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, // 20 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, // 30 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, // 50 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, // 70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // F0 #endregion #region Math Functions /// /// Performs (iA ** iB) % iC. /// /// Base value. /// Exponent value . /// Modulus value. /// The calculated result. private integer ModPow1(integer iA, integer iB, integer iC) { return (int)Math.Pow((int)iA, (int)iB & (int)0xffff) % (int)iC; } /// /// Performs the llModPow2() function. /// /// Value one. /// Value two. /// The modulus value. /// The calculated result. private integer ModPow2(integer iValueX, integer iValueY, integer iModulus) { integer iResult = 0; if (iValueX != 0) { integer k = 1 + (int)Math.Ceiling(Math.Log(Math.Abs(iValueX)) / 0.69314718055994530941723212145818); // ceil(log2(x)) integer w = 32; integer p = w / k; integer r = iValueY / p; integer f = iValueY % p; integer z = 1; if (r) { z = ModPow2(ModPow1(iValueX, p, iModulus), r, iModulus); } if (f) { z = (z * ModPow1(iValueX, f, iModulus)) % iModulus; } iResult = z; } return iResult; } #endregion Math Functions /// /// Used by llListStatistics in generating the statistics. /// /// List of numbers (mixed types possible) to use. /// The numbers added together as a Float type. private List GetListOfNumbers(list lInput) { List lResult = new List(); for (int intI = 0; intI < lInput.Count; intI++) { object objI = lInput[intI]; string strType = objI.GetType().ToString().Replace("LSLEditor.SecondLife+", ""); switch (strType) { case "Float": lResult.Add(Convert.ToDouble((Float)objI)); break; case "System.Int32": lResult.Add(Convert.ToDouble((int)objI)); break; case "System.Double": lResult.Add(Convert.ToDouble((double)objI)); break; case "integer": lResult.Add(Convert.ToDouble((integer)objI)); break; default: break; } } return lResult; } /// /// Calculates the mean of the supplied numbers. /// /// Array of numbers. /// The mean of the supplied numbers as a double type. private double GetAverage(double[] data) { try { double dblDataTotal = 0; for (int i = 0; i < data.Length; i++) { dblDataTotal += data[i]; } return SafeDivide(dblDataTotal, data.Length); } catch (Exception) { throw; } } /// /// Calculates standard deviation. /// /// Array of numbers to work with. /// The standard deviation of the supplied numbers as a double type. public double GetStandardDeviation(double[] dblNumbers) { double dblSum = 0.0, dblSumOfSqrs = 0.0; for (int i = 0; i < dblNumbers.Length; i++) { dblSum += dblNumbers[i]; dblSumOfSqrs += Math.Pow(dblNumbers[i], 2); } double dblTopSum = (dblNumbers.Length * dblSumOfSqrs) - Math.Pow(dblSum, 2); double dblN = (double)dblNumbers.Length; return Math.Sqrt(dblTopSum / (dblN * (dblN - 1))); } /// /// Performs division, only if both values are non-zero. /// /// First number. /// Second number. /// The result of the division, or zero if not performed. private double SafeDivide(double dblValue1, double dblValue2) { double dblResult = 0.0F; try { if ((dblValue1 != 0) && (dblValue2 != 0)) { dblResult = dblValue1 / dblValue2; } } catch { } return dblResult; } /// /// Takes a hexadecimal representation of a number and coverts it to an integer. /// /// Byte to convert. /// Integer value of the supplied byte. private byte HexToInt(byte b) { byte bResult; if (b >= '0' && b <= '9') { bResult = (byte)(b - '0'); } else if ((b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F')) { bResult = (byte)((b & 0x5f) - 0x37); } else { bResult = 0; // error } return bResult; } #endregion } }