/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * 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. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 log4net; using OpenMetaverse; namespace OpenSim.Framework { public static class SLUtil { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// <summary> /// Asset types used only in OpenSim. /// To avoid clashing with the code numbers used in Second Life, use only negative numbers here. /// </summary> public enum OpenSimAssetType : sbyte { Material = -2 } #region SL / file extension / content-type conversions /// <summary> /// Returns the Enum entry corresponding to the given code, regardless of whether it belongs /// to the AssetType or OpenSimAssetType enums. /// </summary> public static object AssetTypeFromCode(sbyte assetType) { if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType)) return (OpenMetaverse.AssetType)assetType; else if (Enum.IsDefined(typeof(OpenSimAssetType), assetType)) return (OpenSimAssetType)assetType; else return OpenMetaverse.AssetType.Unknown; } private class TypeMapping { private sbyte assetType; private InventoryType inventoryType; private string contentType; private string contentType2; private string extension; public sbyte AssetTypeCode { get { return assetType; } } public object AssetType { get { return AssetTypeFromCode(assetType); } } public InventoryType InventoryType { get { return inventoryType; } } public string ContentType { get { return contentType; } } public string ContentType2 { get { return contentType2; } } public string Extension { get { return extension; } } private TypeMapping(sbyte assetType, InventoryType inventoryType, string contentType, string contentType2, string extension) { this.assetType = assetType; this.inventoryType = inventoryType; this.contentType = contentType; this.contentType2 = contentType2; this.extension = extension; } public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string contentType2, string extension) : this((sbyte)assetType, inventoryType, contentType, contentType2, extension) { } public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string extension) : this((sbyte)assetType, inventoryType, contentType, null, extension) { } public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension) : this((sbyte)assetType, inventoryType, contentType, null, extension) { } } /// <summary> /// Maps between AssetType, InventoryType and Content-Type. /// Where more than one possibility exists, the first one takes precedence. E.g.: /// AssetType "AssetType.Texture" -> Content-Type "image-xj2c" /// Content-Type "image/x-j2c" -> InventoryType "InventoryType.Texture" /// </summary> private static TypeMapping[] MAPPINGS = new TypeMapping[] { new TypeMapping(AssetType.Unknown, InventoryType.Unknown, "application/octet-stream", "bin"), new TypeMapping(AssetType.Texture, InventoryType.Texture, "image/x-j2c", "image/jp2", "j2c"), new TypeMapping(AssetType.Texture, InventoryType.Snapshot, "image/x-j2c", "image/jp2", "j2c"), new TypeMapping(AssetType.TextureTGA, InventoryType.Texture, "image/tga", "tga"), new TypeMapping(AssetType.ImageTGA, InventoryType.Texture, "image/tga", "tga"), new TypeMapping(AssetType.ImageJPEG, InventoryType.Texture, "image/jpeg", "jpg"), new TypeMapping(AssetType.Sound, InventoryType.Sound, "audio/ogg", "application/ogg", "ogg"), new TypeMapping(AssetType.SoundWAV, InventoryType.Sound, "audio/x-wav", "wav"), new TypeMapping(AssetType.CallingCard, InventoryType.CallingCard, "application/vnd.ll.callingcard", "application/x-metaverse-callingcard", "callingcard"), new TypeMapping(AssetType.Landmark, InventoryType.Landmark, "application/vnd.ll.landmark", "application/x-metaverse-landmark", "landmark"), new TypeMapping(AssetType.Clothing, InventoryType.Wearable, "application/vnd.ll.clothing", "application/x-metaverse-clothing", "clothing"), new TypeMapping(AssetType.Object, InventoryType.Object, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"), new TypeMapping(AssetType.Object, InventoryType.Attachment, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"), new TypeMapping(AssetType.Notecard, InventoryType.Notecard, "application/vnd.ll.notecard", "application/x-metaverse-notecard", "notecard"), new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"), new TypeMapping(AssetType.RootFolder, InventoryType.RootCategory, "application/vnd.ll.rootfolder", "rootfolder"), new TypeMapping(AssetType.LSLText, InventoryType.LSL, "application/vnd.ll.lsltext", "application/x-metaverse-lsl", "lsl"), new TypeMapping(AssetType.LSLBytecode, InventoryType.LSL, "application/vnd.ll.lslbyte", "application/x-metaverse-lso", "lso"), new TypeMapping(AssetType.Bodypart, InventoryType.Wearable, "application/vnd.ll.bodypart", "application/x-metaverse-bodypart", "bodypart"), new TypeMapping(AssetType.TrashFolder, InventoryType.Folder, "application/vnd.ll.trashfolder", "trashfolder"), new TypeMapping(AssetType.SnapshotFolder, InventoryType.Folder, "application/vnd.ll.snapshotfolder", "snapshotfolder"), new TypeMapping(AssetType.LostAndFoundFolder, InventoryType.Folder, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"), new TypeMapping(AssetType.Animation, InventoryType.Animation, "application/vnd.ll.animation", "application/x-metaverse-animation", "animation"), new TypeMapping(AssetType.Gesture, InventoryType.Gesture, "application/vnd.ll.gesture", "application/x-metaverse-gesture", "gesture"), new TypeMapping(AssetType.Simstate, InventoryType.Snapshot, "application/x-metaverse-simstate", "simstate"), new TypeMapping(AssetType.FavoriteFolder, InventoryType.Unknown, "application/vnd.ll.favoritefolder", "favoritefolder"), new TypeMapping(AssetType.Link, InventoryType.Unknown, "application/vnd.ll.link", "link"), new TypeMapping(AssetType.LinkFolder, InventoryType.Unknown, "application/vnd.ll.linkfolder", "linkfolder"), new TypeMapping(AssetType.CurrentOutfitFolder, InventoryType.Unknown, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"), new TypeMapping(AssetType.OutfitFolder, InventoryType.Unknown, "application/vnd.ll.outfitfolder", "outfitfolder"), new TypeMapping(AssetType.MyOutfitsFolder, InventoryType.Unknown, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"), new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"), new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material") }; private static Dictionary<sbyte, string> asset2Content; private static Dictionary<sbyte, string> asset2Extension; private static Dictionary<InventoryType, string> inventory2Content; private static Dictionary<string, sbyte> content2Asset; private static Dictionary<string, InventoryType> content2Inventory; static SLUtil() { asset2Content = new Dictionary<sbyte, string>(); asset2Extension = new Dictionary<sbyte, string>(); inventory2Content = new Dictionary<InventoryType, string>(); content2Asset = new Dictionary<string, sbyte>(); content2Inventory = new Dictionary<string, InventoryType>(); foreach (TypeMapping mapping in MAPPINGS) { sbyte assetType = mapping.AssetTypeCode; if (!asset2Content.ContainsKey(assetType)) asset2Content.Add(assetType, mapping.ContentType); if (!asset2Extension.ContainsKey(assetType)) asset2Extension.Add(assetType, mapping.Extension); if (!inventory2Content.ContainsKey(mapping.InventoryType)) inventory2Content.Add(mapping.InventoryType, mapping.ContentType); if (!content2Asset.ContainsKey(mapping.ContentType)) content2Asset.Add(mapping.ContentType, assetType); if (!content2Inventory.ContainsKey(mapping.ContentType)) content2Inventory.Add(mapping.ContentType, mapping.InventoryType); if (mapping.ContentType2 != null) { if (!content2Asset.ContainsKey(mapping.ContentType2)) content2Asset.Add(mapping.ContentType2, assetType); if (!content2Inventory.ContainsKey(mapping.ContentType2)) content2Inventory.Add(mapping.ContentType2, mapping.InventoryType); } } } public static string SLAssetTypeToContentType(int assetType) { string contentType; if (!asset2Content.TryGetValue((sbyte)assetType, out contentType)) contentType = asset2Content[(sbyte)AssetType.Unknown]; return contentType; } public static string SLInvTypeToContentType(int invType) { string contentType; if (!inventory2Content.TryGetValue((InventoryType)invType, out contentType)) contentType = inventory2Content[InventoryType.Unknown]; return contentType; } public static sbyte ContentTypeToSLAssetType(string contentType) { sbyte assetType; if (!content2Asset.TryGetValue(contentType, out assetType)) assetType = (sbyte)AssetType.Unknown; return (sbyte)assetType; } public static sbyte ContentTypeToSLInvType(string contentType) { InventoryType invType; if (!content2Inventory.TryGetValue(contentType, out invType)) invType = InventoryType.Unknown; return (sbyte)invType; } public static string SLAssetTypeToExtension(int assetType) { string extension; if (!asset2Extension.TryGetValue((sbyte)assetType, out extension)) extension = asset2Extension[(sbyte)AssetType.Unknown]; return extension; } #endregion SL / file extension / content-type conversions /// <summary> /// Parse a notecard in Linden format to a string of ordinary text. /// </summary> /// <param name="rawInput"></param> /// <returns></returns> public static string ParseNotecardToString(string rawInput) { string[] output = ParseNotecardToList(rawInput).ToArray(); // foreach (string line in output) // m_log.DebugFormat("[PARSE NOTECARD]: ParseNotecardToString got line {0}", line); return string.Join("\n", output); } /// <summary> /// Parse a notecard in Linden format to a list of ordinary lines. /// </summary> /// <param name="rawInput"></param> /// <returns></returns> public static List<string> ParseNotecardToList(string rawInput) { string[] input; int idx = 0; int level = 0; List<string> output = new List<string>(); string[] words; //The Linden format always ends with a } after the input data. //Strip off trailing } so there is nothing after the input data. int i = rawInput.LastIndexOf("}"); rawInput = rawInput.Remove(i, rawInput.Length-i); input = rawInput.Replace("\r", "").Split('\n'); while (idx < input.Length) { if (input[idx] == "{") { level++; idx++; continue; } if (input[idx]== "}") { level--; idx++; continue; } switch (level) { case 0: words = input[idx].Split(' '); // Linden text ver // Notecards are created *really* empty. Treat that as "no text" (just like after saving an empty notecard) if (words.Length < 3) return output; int version = int.Parse(words[3]); if (version != 2) return output; break; case 1: words = input[idx].Split(' '); if (words[0] == "LLEmbeddedItems") break; if (words[0] == "Text") { idx++; //Now points to first line of notecard text //Number of lines in notecard. int lines = input.Length - idx; int line = 0; while (line < lines) { // m_log.DebugFormat("[PARSE NOTECARD]: Adding line {0}", input[idx]); output.Add(input[idx]); idx++; line++; } return output; } break; case 2: words = input[idx].Split(' '); // count if (words[0] == "count") { int c = int.Parse(words[1]); if (c > 0) return output; break; } break; } idx++; } return output; } } }