SLUtil.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  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 sbyte 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 sbyte 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, sbyte 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, sbyte 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 contentType2, string extension)
  101. : this((sbyte)assetType, (sbyte)inventoryType, contentType, contentType2, extension)
  102. {
  103. }
  104. public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string extension)
  105. : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension)
  106. {
  107. }
  108. public TypeMapping(AssetType assetType, FolderType inventoryType, string contentType, string extension)
  109. : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension)
  110. {
  111. }
  112. public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension)
  113. : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension)
  114. {
  115. }
  116. }
  117. /// <summary>
  118. /// Maps between AssetType, InventoryType and Content-Type.
  119. /// Where more than one possibility exists, the first one takes precedence. E.g.:
  120. /// AssetType "AssetType.Texture" -> Content-Type "image-xj2c"
  121. /// Content-Type "image/x-j2c" -> InventoryType "InventoryType.Texture"
  122. /// </summary>
  123. private static TypeMapping[] MAPPINGS = new TypeMapping[] {
  124. new TypeMapping(AssetType.Unknown, InventoryType.Unknown, "application/octet-stream", "bin"),
  125. new TypeMapping(AssetType.Texture, InventoryType.Texture, "image/x-j2c", "image/jp2", "j2c"),
  126. new TypeMapping(AssetType.Texture, InventoryType.Snapshot, "image/x-j2c", "image/jp2", "j2c"),
  127. new TypeMapping(AssetType.TextureTGA, InventoryType.Texture, "image/tga", "tga"),
  128. new TypeMapping(AssetType.ImageTGA, InventoryType.Texture, "image/tga", "tga"),
  129. new TypeMapping(AssetType.ImageJPEG, InventoryType.Texture, "image/jpeg", "jpg"),
  130. new TypeMapping(AssetType.Sound, InventoryType.Sound, "audio/ogg", "application/ogg", "ogg"),
  131. new TypeMapping(AssetType.SoundWAV, InventoryType.Sound, "audio/x-wav", "wav"),
  132. new TypeMapping(AssetType.CallingCard, InventoryType.CallingCard, "application/vnd.ll.callingcard", "application/x-metaverse-callingcard", "callingcard"),
  133. new TypeMapping(AssetType.Landmark, InventoryType.Landmark, "application/vnd.ll.landmark", "application/x-metaverse-landmark", "landmark"),
  134. new TypeMapping(AssetType.Clothing, InventoryType.Wearable, "application/vnd.ll.clothing", "application/x-metaverse-clothing", "clothing"),
  135. new TypeMapping(AssetType.Object, InventoryType.Object, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"),
  136. new TypeMapping(AssetType.Object, InventoryType.Attachment, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"),
  137. new TypeMapping(AssetType.Notecard, InventoryType.Notecard, "application/vnd.ll.notecard", "application/x-metaverse-notecard", "notecard"),
  138. new TypeMapping(AssetType.LSLText, InventoryType.LSL, "application/vnd.ll.lsltext", "application/x-metaverse-lsl", "lsl"),
  139. new TypeMapping(AssetType.LSLBytecode, InventoryType.LSL, "application/vnd.ll.lslbyte", "application/x-metaverse-lso", "lso"),
  140. new TypeMapping(AssetType.Bodypart, InventoryType.Wearable, "application/vnd.ll.bodypart", "application/x-metaverse-bodypart", "bodypart"),
  141. new TypeMapping(AssetType.Animation, InventoryType.Animation, "application/vnd.ll.animation", "application/x-metaverse-animation", "animation"),
  142. new TypeMapping(AssetType.Gesture, InventoryType.Gesture, "application/vnd.ll.gesture", "application/x-metaverse-gesture", "gesture"),
  143. new TypeMapping(AssetType.Simstate, InventoryType.Snapshot, "application/x-metaverse-simstate", "simstate"),
  144. new TypeMapping(AssetType.Link, InventoryType.Unknown, "application/vnd.ll.link", "link"),
  145. new TypeMapping(AssetType.LinkFolder, InventoryType.Unknown, "application/vnd.ll.linkfolder", "linkfolder"),
  146. new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"),
  147. // The next few items are about inventory folders
  148. new TypeMapping(AssetType.Folder, FolderType.None, "application/vnd.ll.folder", "folder"),
  149. new TypeMapping(AssetType.Folder, FolderType.Root, "application/vnd.ll.rootfolder", "rootfolder"),
  150. new TypeMapping(AssetType.Folder, FolderType.Trash, "application/vnd.ll.trashfolder", "trashfolder"),
  151. new TypeMapping(AssetType.Folder, FolderType.Snapshot, "application/vnd.ll.snapshotfolder", "snapshotfolder"),
  152. new TypeMapping(AssetType.Folder, FolderType.LostAndFound, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"),
  153. new TypeMapping(AssetType.Folder, FolderType.Favorites, "application/vnd.ll.favoritefolder", "favoritefolder"),
  154. new TypeMapping(AssetType.Folder, FolderType.CurrentOutfit, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"),
  155. new TypeMapping(AssetType.Folder, FolderType.Outfit, "application/vnd.ll.outfitfolder", "outfitfolder"),
  156. new TypeMapping(AssetType.Folder, FolderType.MyOutfits, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"),
  157. // This next mappping is an asset to inventory item mapping.
  158. // Note: LL stores folders as assets of type Folder = 8, and it has a corresponding InventoryType = 8
  159. // OpenSim doesn't store folders as assets, so this mapping should only be used when parsing things from the viewer to the server
  160. new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"),
  161. // OpenSim specific
  162. new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material")
  163. };
  164. private static Dictionary<sbyte, string> asset2Content;
  165. private static Dictionary<sbyte, string> asset2Extension;
  166. private static Dictionary<sbyte, string> inventory2Content;
  167. private static Dictionary<string, sbyte> content2Asset;
  168. private static Dictionary<string, sbyte> content2Inventory;
  169. static SLUtil()
  170. {
  171. asset2Content = new Dictionary<sbyte, string>();
  172. asset2Extension = new Dictionary<sbyte, string>();
  173. inventory2Content = new Dictionary<sbyte, string>();
  174. content2Asset = new Dictionary<string, sbyte>();
  175. content2Inventory = new Dictionary<string, sbyte>();
  176. foreach (TypeMapping mapping in MAPPINGS)
  177. {
  178. sbyte assetType = mapping.AssetTypeCode;
  179. if (!asset2Content.ContainsKey(assetType))
  180. asset2Content.Add(assetType, mapping.ContentType);
  181. if (!asset2Extension.ContainsKey(assetType))
  182. asset2Extension.Add(assetType, mapping.Extension);
  183. if (!inventory2Content.ContainsKey(mapping.InventoryType))
  184. inventory2Content.Add(mapping.InventoryType, mapping.ContentType);
  185. if (!content2Asset.ContainsKey(mapping.ContentType))
  186. content2Asset.Add(mapping.ContentType, assetType);
  187. if (!content2Inventory.ContainsKey(mapping.ContentType))
  188. content2Inventory.Add(mapping.ContentType, mapping.InventoryType);
  189. if (mapping.ContentType2 != null)
  190. {
  191. if (!content2Asset.ContainsKey(mapping.ContentType2))
  192. content2Asset.Add(mapping.ContentType2, assetType);
  193. if (!content2Inventory.ContainsKey(mapping.ContentType2))
  194. content2Inventory.Add(mapping.ContentType2, mapping.InventoryType);
  195. }
  196. }
  197. }
  198. public static string SLAssetTypeToContentType(int assetType)
  199. {
  200. string contentType;
  201. if (!asset2Content.TryGetValue((sbyte)assetType, out contentType))
  202. contentType = asset2Content[(sbyte)AssetType.Unknown];
  203. return contentType;
  204. }
  205. public static string SLInvTypeToContentType(int invType)
  206. {
  207. string contentType;
  208. if (!inventory2Content.TryGetValue((sbyte)invType, out contentType))
  209. contentType = inventory2Content[(sbyte)InventoryType.Unknown];
  210. return contentType;
  211. }
  212. public static sbyte ContentTypeToSLAssetType(string contentType)
  213. {
  214. sbyte assetType;
  215. if (!content2Asset.TryGetValue(contentType, out assetType))
  216. assetType = (sbyte)AssetType.Unknown;
  217. return (sbyte)assetType;
  218. }
  219. public static sbyte ContentTypeToSLInvType(string contentType)
  220. {
  221. sbyte invType;
  222. if (!content2Inventory.TryGetValue(contentType, out invType))
  223. invType = (sbyte)InventoryType.Unknown;
  224. return (sbyte)invType;
  225. }
  226. public static string SLAssetTypeToExtension(int assetType)
  227. {
  228. string extension;
  229. if (!asset2Extension.TryGetValue((sbyte)assetType, out extension))
  230. extension = asset2Extension[(sbyte)AssetType.Unknown];
  231. return extension;
  232. }
  233. #endregion SL / file extension / content-type conversions
  234. private class NotecardReader
  235. {
  236. private string rawInput;
  237. private int lineNumber;
  238. public int LineNumber
  239. {
  240. get
  241. {
  242. return lineNumber;
  243. }
  244. }
  245. public NotecardReader(string _rawInput)
  246. {
  247. rawInput = (string)_rawInput.Clone();
  248. lineNumber = 0;
  249. }
  250. public string getLine()
  251. {
  252. if(rawInput.Length == 0)
  253. {
  254. throw new NotANotecardFormatException(lineNumber + 1);
  255. }
  256. int pos = rawInput.IndexOf('\n');
  257. if(pos < 0)
  258. {
  259. pos = rawInput.Length;
  260. }
  261. /* cut line from rest */
  262. ++lineNumber;
  263. string line = rawInput.Substring(0, pos);
  264. if (pos + 1 >= rawInput.Length)
  265. {
  266. rawInput = string.Empty;
  267. }
  268. else
  269. {
  270. rawInput = rawInput.Substring(pos + 1);
  271. }
  272. /* clean up line from double spaces and tabs */
  273. line = line.Replace("\t", " ");
  274. while(line.IndexOf(" ") >= 0)
  275. {
  276. line = line.Replace(" ", " ");
  277. }
  278. return line.Replace("\r", "").Trim();
  279. }
  280. public string getBlock(int length)
  281. {
  282. /* cut line from rest */
  283. if(length > rawInput.Length)
  284. {
  285. throw new NotANotecardFormatException(lineNumber);
  286. }
  287. string line = rawInput.Substring(0, length);
  288. rawInput = rawInput.Substring(length);
  289. return line;
  290. }
  291. }
  292. public class NotANotecardFormatException : Exception
  293. {
  294. public int lineNumber;
  295. public NotANotecardFormatException(int _lineNumber)
  296. : base()
  297. {
  298. lineNumber = _lineNumber;
  299. }
  300. }
  301. private static void skipSection(NotecardReader reader)
  302. {
  303. if (reader.getLine() != "{")
  304. throw new NotANotecardFormatException(reader.LineNumber);
  305. string line;
  306. while ((line = reader.getLine()) != "}")
  307. {
  308. if(line.IndexOf('{')>=0)
  309. {
  310. throw new NotANotecardFormatException(reader.LineNumber);
  311. }
  312. }
  313. }
  314. private static void skipInventoryItem(NotecardReader reader)
  315. {
  316. if (reader.getLine() != "{")
  317. throw new NotANotecardFormatException(reader.LineNumber);
  318. string line;
  319. while((line = reader.getLine()) != "}")
  320. {
  321. string[] data = line.Split(' ');
  322. if(data.Length == 0)
  323. {
  324. continue;
  325. }
  326. if(data[0] == "permissions")
  327. {
  328. skipSection(reader);
  329. }
  330. else if(data[0] == "sale_info")
  331. {
  332. skipSection(reader);
  333. }
  334. else if (line.IndexOf('{') >= 0)
  335. {
  336. throw new NotANotecardFormatException(reader.LineNumber);
  337. }
  338. }
  339. }
  340. private static void skipInventoryItems(NotecardReader reader)
  341. {
  342. if(reader.getLine() != "{")
  343. {
  344. throw new NotANotecardFormatException(reader.LineNumber);
  345. }
  346. string line;
  347. while((line = reader.getLine()) != "}")
  348. {
  349. string[] data = line.Split(' ');
  350. if(data.Length == 0)
  351. {
  352. continue;
  353. }
  354. if(data[0] == "inv_item")
  355. {
  356. skipInventoryItem(reader);
  357. }
  358. else if (line.IndexOf('{') >= 0)
  359. {
  360. throw new NotANotecardFormatException(reader.LineNumber);
  361. }
  362. }
  363. }
  364. private static void skipInventory(NotecardReader reader)
  365. {
  366. if (reader.getLine() != "{")
  367. throw new NotANotecardFormatException(reader.LineNumber);
  368. string line;
  369. while((line = reader.getLine()) != "}")
  370. {
  371. string[] data = line.Split(' ');
  372. if(data[0] == "count")
  373. {
  374. int count = Int32.Parse(data[1]);
  375. for(int i = 0; i < count; ++i)
  376. {
  377. skipInventoryItems(reader);
  378. }
  379. }
  380. else if (line.IndexOf('{') >= 0)
  381. {
  382. throw new NotANotecardFormatException(reader.LineNumber);
  383. }
  384. }
  385. }
  386. private static string readNotecardText(NotecardReader reader)
  387. {
  388. if (reader.getLine() != "{")
  389. throw new NotANotecardFormatException(reader.LineNumber);
  390. string notecardString = string.Empty;
  391. string line;
  392. while((line = reader.getLine()) != "}")
  393. {
  394. string[] data = line.Split(' ');
  395. if (data.Length == 0)
  396. {
  397. continue;
  398. }
  399. if (data[0] == "LLEmbeddedItems")
  400. {
  401. skipInventory(reader);
  402. }
  403. else if(data[0] == "Text" && data.Length == 3)
  404. {
  405. int length = Int32.Parse(data[2]);
  406. notecardString = reader.getBlock(length);
  407. }
  408. else if (line.IndexOf('{') >= 0)
  409. {
  410. throw new NotANotecardFormatException(reader.LineNumber);
  411. }
  412. }
  413. return notecardString;
  414. }
  415. private static string readNotecard(byte[] rawInput)
  416. {
  417. string rawIntermedInput = string.Empty;
  418. /* make up a Raw Encoding here */
  419. foreach(byte c in rawInput)
  420. {
  421. char d = (char)c;
  422. rawIntermedInput += d;
  423. }
  424. NotecardReader reader = new NotecardReader(rawIntermedInput);
  425. string line;
  426. try
  427. {
  428. line = reader.getLine();
  429. }
  430. catch(Exception)
  431. {
  432. return System.Text.Encoding.UTF8.GetString(rawInput);
  433. }
  434. string[] versioninfo = line.Split(' ');
  435. if(versioninfo.Length < 3)
  436. {
  437. return System.Text.Encoding.UTF8.GetString(rawInput);
  438. }
  439. else if(versioninfo[0] != "Linden" || versioninfo[1] != "text")
  440. {
  441. return System.Text.Encoding.UTF8.GetString(rawInput);
  442. }
  443. else
  444. {
  445. /* now we actually decode the Encoding, before we needed it in raw */
  446. string o = readNotecardText(reader);
  447. byte[] a = new byte[o.Length];
  448. for(int i = 0; i < o.Length; ++i)
  449. {
  450. a[i] = (byte)o[i];
  451. }
  452. return System.Text.Encoding.UTF8.GetString(a);
  453. }
  454. }
  455. /// <summary>
  456. /// Parse a notecard in Linden format to a string of ordinary text.
  457. /// </summary>
  458. /// <param name="rawInput"></param>
  459. /// <returns></returns>
  460. public static string ParseNotecardToString(byte[] rawInput)
  461. {
  462. return readNotecard(rawInput);
  463. }
  464. /// <summary>
  465. /// Parse a notecard in Linden format to a list of ordinary lines.
  466. /// </summary>
  467. /// <param name="rawInput"></param>
  468. /// <returns></returns>
  469. public static string[] ParseNotecardToArray(byte[] rawInput)
  470. {
  471. return readNotecard(rawInput).Replace("\r", "").Split('\n');
  472. }
  473. }
  474. }