SLUtil.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using OpenMetaverse;
  28. using System;
  29. using System.Collections.Generic;
  30. namespace OpenSim.Framework
  31. {
  32. public static class SLUtil
  33. {
  34. // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  35. /// <summary>
  36. /// Asset types used only in OpenSim.
  37. /// To avoid clashing with the code numbers used in Second Life, use only negative numbers here.
  38. /// </summary>
  39. public enum OpenSimAssetType : sbyte
  40. {
  41. Material = -2
  42. }
  43. #region SL / file extension / content-type conversions
  44. /// <summary>
  45. /// Returns the Enum entry corresponding to the given code, regardless of whether it belongs
  46. /// to the AssetType or OpenSimAssetType enums.
  47. /// </summary>
  48. public static object AssetTypeFromCode(sbyte assetType)
  49. {
  50. if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType))
  51. return (OpenMetaverse.AssetType)assetType;
  52. else if (Enum.IsDefined(typeof(OpenSimAssetType), assetType))
  53. return (OpenSimAssetType)assetType;
  54. else
  55. return OpenMetaverse.AssetType.Unknown;
  56. }
  57. private class TypeMapping
  58. {
  59. private sbyte assetType;
  60. private InventoryType inventoryType;
  61. private string contentType;
  62. private string contentType2;
  63. private string extension;
  64. public sbyte AssetTypeCode
  65. {
  66. get { return assetType; }
  67. }
  68. public object AssetType
  69. {
  70. get { return AssetTypeFromCode(assetType); }
  71. }
  72. public InventoryType InventoryType
  73. {
  74. get { return inventoryType; }
  75. }
  76. public string ContentType
  77. {
  78. get { return contentType; }
  79. }
  80. public string ContentType2
  81. {
  82. get { return contentType2; }
  83. }
  84. public string Extension
  85. {
  86. get { return extension; }
  87. }
  88. private TypeMapping(sbyte assetType, InventoryType inventoryType, string contentType, string contentType2, string extension)
  89. {
  90. this.assetType = assetType;
  91. this.inventoryType = inventoryType;
  92. this.contentType = contentType;
  93. this.contentType2 = contentType2;
  94. this.extension = extension;
  95. }
  96. public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string contentType2, string extension)
  97. : this((sbyte)assetType, inventoryType, contentType, contentType2, extension)
  98. {
  99. }
  100. public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string extension)
  101. : this((sbyte)assetType, inventoryType, contentType, null, extension)
  102. {
  103. }
  104. public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension)
  105. : this((sbyte)assetType, inventoryType, contentType, null, extension)
  106. {
  107. }
  108. }
  109. /// <summary>
  110. /// Maps between AssetType, InventoryType and Content-Type.
  111. /// Where more than one possibility exists, the first one takes precedence. E.g.:
  112. /// AssetType "AssetType.Texture" -> Content-Type "image-xj2c"
  113. /// Content-Type "image/x-j2c" -> InventoryType "InventoryType.Texture"
  114. /// </summary>
  115. private static TypeMapping[] MAPPINGS = new TypeMapping[] {
  116. new TypeMapping(AssetType.Unknown, InventoryType.Unknown, "application/octet-stream", "bin"),
  117. new TypeMapping(AssetType.Texture, InventoryType.Texture, "image/x-j2c", "image/jp2", "j2c"),
  118. new TypeMapping(AssetType.Texture, InventoryType.Snapshot, "image/x-j2c", "image/jp2", "j2c"),
  119. new TypeMapping(AssetType.TextureTGA, InventoryType.Texture, "image/tga", "tga"),
  120. new TypeMapping(AssetType.ImageTGA, InventoryType.Texture, "image/tga", "tga"),
  121. new TypeMapping(AssetType.ImageJPEG, InventoryType.Texture, "image/jpeg", "jpg"),
  122. new TypeMapping(AssetType.Sound, InventoryType.Sound, "audio/ogg", "application/ogg", "ogg"),
  123. new TypeMapping(AssetType.SoundWAV, InventoryType.Sound, "audio/x-wav", "wav"),
  124. new TypeMapping(AssetType.CallingCard, InventoryType.CallingCard, "application/vnd.ll.callingcard", "application/x-metaverse-callingcard", "callingcard"),
  125. new TypeMapping(AssetType.Landmark, InventoryType.Landmark, "application/vnd.ll.landmark", "application/x-metaverse-landmark", "landmark"),
  126. new TypeMapping(AssetType.Clothing, InventoryType.Wearable, "application/vnd.ll.clothing", "application/x-metaverse-clothing", "clothing"),
  127. new TypeMapping(AssetType.Object, InventoryType.Object, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"),
  128. new TypeMapping(AssetType.Object, InventoryType.Attachment, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"),
  129. new TypeMapping(AssetType.Notecard, InventoryType.Notecard, "application/vnd.ll.notecard", "application/x-metaverse-notecard", "notecard"),
  130. new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"),
  131. new TypeMapping(AssetType.RootFolder, InventoryType.RootCategory, "application/vnd.ll.rootfolder", "rootfolder"),
  132. new TypeMapping(AssetType.LSLText, InventoryType.LSL, "application/vnd.ll.lsltext", "application/x-metaverse-lsl", "lsl"),
  133. new TypeMapping(AssetType.LSLBytecode, InventoryType.LSL, "application/vnd.ll.lslbyte", "application/x-metaverse-lso", "lso"),
  134. new TypeMapping(AssetType.Bodypart, InventoryType.Wearable, "application/vnd.ll.bodypart", "application/x-metaverse-bodypart", "bodypart"),
  135. new TypeMapping(AssetType.TrashFolder, InventoryType.Folder, "application/vnd.ll.trashfolder", "trashfolder"),
  136. new TypeMapping(AssetType.SnapshotFolder, InventoryType.Folder, "application/vnd.ll.snapshotfolder", "snapshotfolder"),
  137. new TypeMapping(AssetType.LostAndFoundFolder, InventoryType.Folder, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"),
  138. new TypeMapping(AssetType.Animation, InventoryType.Animation, "application/vnd.ll.animation", "application/x-metaverse-animation", "animation"),
  139. new TypeMapping(AssetType.Gesture, InventoryType.Gesture, "application/vnd.ll.gesture", "application/x-metaverse-gesture", "gesture"),
  140. new TypeMapping(AssetType.Simstate, InventoryType.Snapshot, "application/x-metaverse-simstate", "simstate"),
  141. new TypeMapping(AssetType.FavoriteFolder, InventoryType.Unknown, "application/vnd.ll.favoritefolder", "favoritefolder"),
  142. new TypeMapping(AssetType.Link, InventoryType.Unknown, "application/vnd.ll.link", "link"),
  143. new TypeMapping(AssetType.LinkFolder, InventoryType.Unknown, "application/vnd.ll.linkfolder", "linkfolder"),
  144. new TypeMapping(AssetType.CurrentOutfitFolder, InventoryType.Unknown, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"),
  145. new TypeMapping(AssetType.OutfitFolder, InventoryType.Unknown, "application/vnd.ll.outfitfolder", "outfitfolder"),
  146. new TypeMapping(AssetType.MyOutfitsFolder, InventoryType.Unknown, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"),
  147. new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"),
  148. new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material")
  149. };
  150. private static Dictionary<sbyte, string> asset2Content;
  151. private static Dictionary<sbyte, string> asset2Extension;
  152. private static Dictionary<InventoryType, string> inventory2Content;
  153. private static Dictionary<string, sbyte> content2Asset;
  154. private static Dictionary<string, InventoryType> content2Inventory;
  155. static SLUtil()
  156. {
  157. asset2Content = new Dictionary<sbyte, string>();
  158. asset2Extension = new Dictionary<sbyte, string>();
  159. inventory2Content = new Dictionary<InventoryType, string>();
  160. content2Asset = new Dictionary<string, sbyte>();
  161. content2Inventory = new Dictionary<string, InventoryType>();
  162. foreach (TypeMapping mapping in MAPPINGS)
  163. {
  164. sbyte assetType = mapping.AssetTypeCode;
  165. if (!asset2Content.ContainsKey(assetType))
  166. asset2Content.Add(assetType, mapping.ContentType);
  167. if (!asset2Extension.ContainsKey(assetType))
  168. asset2Extension.Add(assetType, mapping.Extension);
  169. if (!inventory2Content.ContainsKey(mapping.InventoryType))
  170. inventory2Content.Add(mapping.InventoryType, mapping.ContentType);
  171. if (!content2Asset.ContainsKey(mapping.ContentType))
  172. content2Asset.Add(mapping.ContentType, assetType);
  173. if (!content2Inventory.ContainsKey(mapping.ContentType))
  174. content2Inventory.Add(mapping.ContentType, mapping.InventoryType);
  175. if (mapping.ContentType2 != null)
  176. {
  177. if (!content2Asset.ContainsKey(mapping.ContentType2))
  178. content2Asset.Add(mapping.ContentType2, assetType);
  179. if (!content2Inventory.ContainsKey(mapping.ContentType2))
  180. content2Inventory.Add(mapping.ContentType2, mapping.InventoryType);
  181. }
  182. }
  183. }
  184. public static string SLAssetTypeToContentType(int assetType)
  185. {
  186. string contentType;
  187. if (!asset2Content.TryGetValue((sbyte)assetType, out contentType))
  188. contentType = asset2Content[(sbyte)AssetType.Unknown];
  189. return contentType;
  190. }
  191. public static string SLInvTypeToContentType(int invType)
  192. {
  193. string contentType;
  194. if (!inventory2Content.TryGetValue((InventoryType)invType, out contentType))
  195. contentType = inventory2Content[InventoryType.Unknown];
  196. return contentType;
  197. }
  198. public static sbyte ContentTypeToSLAssetType(string contentType)
  199. {
  200. sbyte assetType;
  201. if (!content2Asset.TryGetValue(contentType, out assetType))
  202. assetType = (sbyte)AssetType.Unknown;
  203. return (sbyte)assetType;
  204. }
  205. public static sbyte ContentTypeToSLInvType(string contentType)
  206. {
  207. InventoryType invType;
  208. if (!content2Inventory.TryGetValue(contentType, out invType))
  209. invType = InventoryType.Unknown;
  210. return (sbyte)invType;
  211. }
  212. public static string SLAssetTypeToExtension(int assetType)
  213. {
  214. string extension;
  215. if (!asset2Extension.TryGetValue((sbyte)assetType, out extension))
  216. extension = asset2Extension[(sbyte)AssetType.Unknown];
  217. return extension;
  218. }
  219. #endregion SL / file extension / content-type conversions
  220. private class NotecardReader
  221. {
  222. private string rawInput;
  223. private int lineNumber;
  224. public int LineNumber
  225. {
  226. get
  227. {
  228. return lineNumber;
  229. }
  230. }
  231. public NotecardReader(string _rawInput)
  232. {
  233. rawInput = (string)_rawInput.Clone();
  234. lineNumber = 0;
  235. }
  236. public string getLine()
  237. {
  238. if(rawInput.Length == 0)
  239. {
  240. throw new NotANotecardFormatException(lineNumber + 1);
  241. }
  242. int pos = rawInput.IndexOf('\n');
  243. if(pos < 0)
  244. {
  245. pos = rawInput.Length;
  246. }
  247. /* cut line from rest */
  248. ++lineNumber;
  249. string line = rawInput.Substring(0, pos);
  250. if (pos + 1 >= rawInput.Length)
  251. {
  252. rawInput = string.Empty;
  253. }
  254. else
  255. {
  256. rawInput = rawInput.Substring(pos + 1);
  257. }
  258. /* clean up line from double spaces and tabs */
  259. line = line.Replace("\t", " ");
  260. while(line.IndexOf(" ") >= 0)
  261. {
  262. line = line.Replace(" ", " ");
  263. }
  264. return line.Replace("\r", "").Trim();
  265. }
  266. public string getBlock(int length)
  267. {
  268. /* cut line from rest */
  269. if(length > rawInput.Length)
  270. {
  271. throw new NotANotecardFormatException(lineNumber);
  272. }
  273. string line = rawInput.Substring(0, length);
  274. rawInput = rawInput.Substring(length);
  275. return line;
  276. }
  277. }
  278. public class NotANotecardFormatException : Exception
  279. {
  280. public int lineNumber;
  281. public NotANotecardFormatException(int _lineNumber)
  282. : base()
  283. {
  284. lineNumber = _lineNumber;
  285. }
  286. }
  287. private static void skipSection(NotecardReader reader)
  288. {
  289. if (reader.getLine() != "{")
  290. throw new NotANotecardFormatException(reader.LineNumber);
  291. string line;
  292. while ((line = reader.getLine()) != "}")
  293. {
  294. if(line.IndexOf('{')>=0)
  295. {
  296. throw new NotANotecardFormatException(reader.LineNumber);
  297. }
  298. }
  299. }
  300. private static void skipInventoryItem(NotecardReader reader)
  301. {
  302. if (reader.getLine() != "{")
  303. throw new NotANotecardFormatException(reader.LineNumber);
  304. string line;
  305. while((line = reader.getLine()) != "}")
  306. {
  307. string[] data = line.Split(' ');
  308. if(data.Length == 0)
  309. {
  310. continue;
  311. }
  312. if(data[0] == "permissions")
  313. {
  314. skipSection(reader);
  315. }
  316. else if(data[0] == "sale_info")
  317. {
  318. skipSection(reader);
  319. }
  320. else if (line.IndexOf('{') >= 0)
  321. {
  322. throw new NotANotecardFormatException(reader.LineNumber);
  323. }
  324. }
  325. }
  326. private static void skipInventoryItems(NotecardReader reader)
  327. {
  328. if(reader.getLine() != "{")
  329. {
  330. throw new NotANotecardFormatException(reader.LineNumber);
  331. }
  332. string line;
  333. while((line = reader.getLine()) != "}")
  334. {
  335. string[] data = line.Split(' ');
  336. if(data.Length == 0)
  337. {
  338. continue;
  339. }
  340. if(data[0] == "inv_item")
  341. {
  342. skipInventoryItem(reader);
  343. }
  344. else if (line.IndexOf('{') >= 0)
  345. {
  346. throw new NotANotecardFormatException(reader.LineNumber);
  347. }
  348. }
  349. }
  350. private static void skipInventory(NotecardReader reader)
  351. {
  352. if (reader.getLine() != "{")
  353. throw new NotANotecardFormatException(reader.LineNumber);
  354. string line;
  355. while((line = reader.getLine()) != "}")
  356. {
  357. string[] data = line.Split(' ');
  358. if(data[0] == "count")
  359. {
  360. int count = Int32.Parse(data[1]);
  361. for(int i = 0; i < count; ++i)
  362. {
  363. skipInventoryItems(reader);
  364. }
  365. }
  366. else if (line.IndexOf('{') >= 0)
  367. {
  368. throw new NotANotecardFormatException(reader.LineNumber);
  369. }
  370. }
  371. }
  372. private static string readNotecardText(NotecardReader reader)
  373. {
  374. if (reader.getLine() != "{")
  375. throw new NotANotecardFormatException(reader.LineNumber);
  376. string notecardString = string.Empty;
  377. string line;
  378. while((line = reader.getLine()) != "}")
  379. {
  380. string[] data = line.Split(' ');
  381. if (data.Length == 0)
  382. {
  383. continue;
  384. }
  385. if (data[0] == "LLEmbeddedItems")
  386. {
  387. skipInventory(reader);
  388. }
  389. else if(data[0] == "Text" && data.Length == 3)
  390. {
  391. int length = Int32.Parse(data[2]);
  392. notecardString = reader.getBlock(length);
  393. }
  394. else if (line.IndexOf('{') >= 0)
  395. {
  396. throw new NotANotecardFormatException(reader.LineNumber);
  397. }
  398. }
  399. return notecardString;
  400. }
  401. private static string readNotecard(byte[] rawInput)
  402. {
  403. string rawIntermedInput = string.Empty;
  404. /* make up a Raw Encoding here */
  405. foreach(byte c in rawInput)
  406. {
  407. char d = (char)c;
  408. rawIntermedInput += d;
  409. }
  410. NotecardReader reader = new NotecardReader(rawIntermedInput);
  411. string line;
  412. try
  413. {
  414. line = reader.getLine();
  415. }
  416. catch(Exception)
  417. {
  418. return System.Text.Encoding.UTF8.GetString(rawInput);
  419. }
  420. string[] versioninfo = line.Split(' ');
  421. if(versioninfo.Length < 3)
  422. {
  423. return System.Text.Encoding.UTF8.GetString(rawInput);
  424. }
  425. else if(versioninfo[0] != "Linden" || versioninfo[1] != "text")
  426. {
  427. return System.Text.Encoding.UTF8.GetString(rawInput);
  428. }
  429. else
  430. {
  431. /* now we actually decode the Encoding, before we needed it in raw */
  432. string o = readNotecardText(reader);
  433. byte[] a = new byte[o.Length];
  434. for(int i = 0; i < o.Length; ++i)
  435. {
  436. a[i] = (byte)o[i];
  437. }
  438. return System.Text.Encoding.UTF8.GetString(a);
  439. }
  440. }
  441. /// <summary>
  442. /// Parse a notecard in Linden format to a string of ordinary text.
  443. /// </summary>
  444. /// <param name="rawInput"></param>
  445. /// <returns></returns>
  446. public static string ParseNotecardToString(byte[] rawInput)
  447. {
  448. return readNotecard(rawInput);
  449. }
  450. /// <summary>
  451. /// Parse a notecard in Linden format to a list of ordinary lines.
  452. /// </summary>
  453. /// <param name="rawInput"></param>
  454. /// <returns></returns>
  455. public static string[] ParseNotecardToArray(byte[] rawInput)
  456. {
  457. return readNotecard(rawInput).Replace("\r", "").Split('\n');
  458. }
  459. }
  460. }