SLUtil.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  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. using System.Globalization;
  31. namespace OpenSim.Framework
  32. {
  33. public static class SLUtil
  34. {
  35. // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  36. /// <summary>
  37. /// Asset types used only in OpenSim.
  38. /// To avoid clashing with the code numbers used in Second Life, use only negative numbers here.
  39. /// </summary>
  40. public enum OpenSimAssetType : sbyte
  41. {
  42. Material = -2
  43. }
  44. #region SL / file extension / content-type conversions
  45. /// <summary>
  46. /// Returns the Enum entry corresponding to the given code, regardless of whether it belongs
  47. /// to the AssetType or OpenSimAssetType enums.
  48. /// </summary>
  49. public static object AssetTypeFromCode(sbyte assetType)
  50. {
  51. if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType))
  52. return (OpenMetaverse.AssetType)assetType;
  53. else if (Enum.IsDefined(typeof(OpenSimAssetType), assetType))
  54. return (OpenSimAssetType)assetType;
  55. else
  56. return OpenMetaverse.AssetType.Unknown;
  57. }
  58. private class TypeMapping
  59. {
  60. private sbyte assetType;
  61. private sbyte inventoryType;
  62. private string contentType;
  63. private string contentType2;
  64. private string extension;
  65. public sbyte AssetTypeCode
  66. {
  67. get { return assetType; }
  68. }
  69. public object AssetType
  70. {
  71. get { return AssetTypeFromCode(assetType); }
  72. }
  73. public sbyte InventoryType
  74. {
  75. get { return inventoryType; }
  76. }
  77. public string ContentType
  78. {
  79. get { return contentType; }
  80. }
  81. public string ContentType2
  82. {
  83. get { return contentType2; }
  84. }
  85. public string Extension
  86. {
  87. get { return extension; }
  88. }
  89. private TypeMapping(sbyte assetType, sbyte inventoryType, string contentType, string contentType2, string extension)
  90. {
  91. this.assetType = assetType;
  92. this.inventoryType = inventoryType;
  93. this.contentType = contentType;
  94. this.contentType2 = contentType2;
  95. this.extension = extension;
  96. }
  97. public TypeMapping(AssetType assetType, sbyte inventoryType, string contentType, string contentType2, string extension)
  98. : this((sbyte)assetType, inventoryType, contentType, contentType2, extension)
  99. {
  100. }
  101. public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string contentType2, string extension)
  102. : this((sbyte)assetType, (sbyte)inventoryType, contentType, contentType2, extension)
  103. {
  104. }
  105. public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string extension)
  106. : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension)
  107. {
  108. }
  109. public TypeMapping(AssetType assetType, FolderType inventoryType, string contentType, string extension)
  110. : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension)
  111. {
  112. }
  113. public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension)
  114. : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension)
  115. {
  116. }
  117. }
  118. /// <summary>
  119. /// Maps between AssetType, InventoryType and Content-Type.
  120. /// Where more than one possibility exists, the first one takes precedence. E.g.:
  121. /// AssetType "AssetType.Texture" -> Content-Type "image-xj2c"
  122. /// Content-Type "image/x-j2c" -> InventoryType "InventoryType.Texture"
  123. /// </summary>
  124. private static TypeMapping[] MAPPINGS = new TypeMapping[] {
  125. new TypeMapping(AssetType.Unknown, InventoryType.Unknown, "application/octet-stream", "bin"),
  126. new TypeMapping(AssetType.Texture, InventoryType.Texture, "image/x-j2c", "image/jp2", "j2c"),
  127. new TypeMapping(AssetType.Texture, InventoryType.Snapshot, "image/x-j2c", "image/jp2", "j2c"),
  128. new TypeMapping(AssetType.TextureTGA, InventoryType.Texture, "image/tga", "tga"),
  129. new TypeMapping(AssetType.ImageTGA, InventoryType.Texture, "image/tga", "tga"),
  130. new TypeMapping(AssetType.ImageJPEG, InventoryType.Texture, "image/jpeg", "jpg"),
  131. new TypeMapping(AssetType.Sound, InventoryType.Sound, "audio/ogg", "application/ogg", "ogg"),
  132. new TypeMapping(AssetType.SoundWAV, InventoryType.Sound, "audio/x-wav", "wav"),
  133. new TypeMapping(AssetType.CallingCard, InventoryType.CallingCard, "application/vnd.ll.callingcard", "application/x-metaverse-callingcard", "callingcard"),
  134. new TypeMapping(AssetType.Landmark, InventoryType.Landmark, "application/vnd.ll.landmark", "application/x-metaverse-landmark", "landmark"),
  135. new TypeMapping(AssetType.Clothing, InventoryType.Wearable, "application/vnd.ll.clothing", "application/x-metaverse-clothing", "clothing"),
  136. new TypeMapping(AssetType.Object, InventoryType.Object, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"),
  137. new TypeMapping(AssetType.Object, InventoryType.Attachment, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"),
  138. new TypeMapping(AssetType.Notecard, InventoryType.Notecard, "application/vnd.ll.notecard", "application/x-metaverse-notecard", "notecard"),
  139. new TypeMapping(AssetType.LSLText, InventoryType.LSL, "application/vnd.ll.lsltext", "application/x-metaverse-lsl", "lsl"),
  140. new TypeMapping(AssetType.LSLBytecode, InventoryType.LSL, "application/vnd.ll.lslbyte", "application/x-metaverse-lso", "lso"),
  141. new TypeMapping(AssetType.Bodypart, InventoryType.Wearable, "application/vnd.ll.bodypart", "application/x-metaverse-bodypart", "bodypart"),
  142. new TypeMapping(AssetType.Animation, InventoryType.Animation, "application/vnd.ll.animation", "application/x-metaverse-animation", "animation"),
  143. new TypeMapping(AssetType.Gesture, InventoryType.Gesture, "application/vnd.ll.gesture", "application/x-metaverse-gesture", "gesture"),
  144. new TypeMapping(AssetType.Simstate, InventoryType.Snapshot, "application/x-metaverse-simstate", "simstate"),
  145. new TypeMapping(AssetType.Link, InventoryType.Unknown, "application/vnd.ll.link", "link"),
  146. new TypeMapping(AssetType.LinkFolder, InventoryType.Unknown, "application/vnd.ll.linkfolder", "linkfolder"),
  147. new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"),
  148. new TypeMapping(AssetType.Material, InventoryType.Material, "application/llsd+xml", "glftmat"),
  149. // The next few items are about inventory folders
  150. new TypeMapping(AssetType.Folder, FolderType.None, "application/vnd.ll.folder", "folder"),
  151. new TypeMapping(AssetType.Folder, FolderType.Root, "application/vnd.ll.rootfolder", "rootfolder"),
  152. new TypeMapping(AssetType.Folder, FolderType.Trash, "application/vnd.ll.trashfolder", "trashfolder"),
  153. new TypeMapping(AssetType.Folder, FolderType.Snapshot, "application/vnd.ll.snapshotfolder", "snapshotfolder"),
  154. new TypeMapping(AssetType.Folder, FolderType.LostAndFound, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"),
  155. new TypeMapping(AssetType.Folder, FolderType.Favorites, "application/vnd.ll.favoritefolder", "favoritefolder"),
  156. new TypeMapping(AssetType.Folder, FolderType.CurrentOutfit, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"),
  157. new TypeMapping(AssetType.Folder, FolderType.Outfit, "application/vnd.ll.outfitfolder", "outfitfolder"),
  158. new TypeMapping(AssetType.Folder, FolderType.MyOutfits, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"),
  159. // This next mappping is an asset to inventory item mapping.
  160. // Note: LL stores folders as assets of type Folder = 8, and it has a corresponding InventoryType = 8
  161. // OpenSim doesn't store folders as assets, so this mapping should only be used when parsing things from the viewer to the server
  162. new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"),
  163. // OpenSim specific
  164. new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material")
  165. };
  166. private static Dictionary<sbyte, string> asset2Content;
  167. private static Dictionary<sbyte, string> asset2Extension;
  168. private static Dictionary<sbyte, string> inventory2Content;
  169. private static Dictionary<string, sbyte> content2Asset;
  170. private static Dictionary<string, sbyte> content2Inventory;
  171. private static Dictionary<string, AssetType> name2Asset = new Dictionary<string, AssetType>()
  172. {
  173. {"texture", AssetType.Texture },
  174. {"sound", AssetType.Sound},
  175. {"callcard", AssetType.CallingCard},
  176. {"landmark", AssetType.Landmark},
  177. {"script", (AssetType)4},
  178. {"clothing", AssetType.Clothing},
  179. {"object", AssetType.Object},
  180. {"notecard", AssetType.Notecard},
  181. {"category", AssetType.Folder},
  182. {"lsltext", AssetType.LSLText},
  183. {"lslbyte", AssetType.LSLBytecode},
  184. {"txtr_tga", AssetType.TextureTGA},
  185. {"bodypart", AssetType.Bodypart},
  186. {"snd_wav", AssetType.SoundWAV},
  187. {"img_tga", AssetType.ImageTGA},
  188. {"jpeg", AssetType.ImageJPEG},
  189. {"animatn", AssetType.Animation},
  190. {"gesture", AssetType.Gesture},
  191. {"simstate", AssetType.Simstate},
  192. {"mesh", AssetType.Mesh},
  193. {"settings", AssetType.Settings},
  194. {"material", AssetType.Material}
  195. };
  196. private static Dictionary<string, FolderType> name2Inventory = new Dictionary<string, FolderType>()
  197. {
  198. {"texture", FolderType.Texture},
  199. {"sound", FolderType.Sound},
  200. {"callcard", FolderType.CallingCard},
  201. {"landmark", FolderType.Landmark},
  202. {"script", (FolderType)4},
  203. {"clothing", FolderType.Clothing},
  204. {"object", FolderType.Object},
  205. {"notecard", FolderType.Notecard},
  206. {"root", FolderType.Root},
  207. {"lsltext", FolderType.LSLText},
  208. {"bodypart", FolderType.BodyPart},
  209. {"trash", FolderType.Trash},
  210. {"snapshot", FolderType.Snapshot},
  211. {"lostandfound", FolderType.LostAndFound},
  212. {"animatn", FolderType.Animation},
  213. {"gesture", FolderType.Gesture},
  214. {"favorites", FolderType.Favorites},
  215. {"currentoutfit", FolderType.CurrentOutfit},
  216. {"outfit", FolderType.Outfit},
  217. {"myoutfits", FolderType.MyOutfits},
  218. {"mesh", FolderType.Mesh},
  219. {"settings", FolderType.Settings},
  220. {"material", FolderType.Material},
  221. {"suitcase", FolderType.Suitcase}
  222. };
  223. static SLUtil()
  224. {
  225. asset2Content = new Dictionary<sbyte, string>();
  226. asset2Extension = new Dictionary<sbyte, string>();
  227. inventory2Content = new Dictionary<sbyte, string>();
  228. content2Asset = new Dictionary<string, sbyte>();
  229. content2Inventory = new Dictionary<string, sbyte>();
  230. foreach (TypeMapping mapping in MAPPINGS)
  231. {
  232. sbyte assetType = mapping.AssetTypeCode;
  233. if (!asset2Content.ContainsKey(assetType))
  234. asset2Content.Add(assetType, mapping.ContentType);
  235. if (!asset2Extension.ContainsKey(assetType))
  236. asset2Extension.Add(assetType, mapping.Extension);
  237. if (!inventory2Content.ContainsKey(mapping.InventoryType))
  238. inventory2Content.Add(mapping.InventoryType, mapping.ContentType);
  239. if (!content2Asset.ContainsKey(mapping.ContentType))
  240. content2Asset.Add(mapping.ContentType, assetType);
  241. if (!content2Inventory.ContainsKey(mapping.ContentType))
  242. content2Inventory.Add(mapping.ContentType, mapping.InventoryType);
  243. if (mapping.ContentType2 != null)
  244. {
  245. if (!content2Asset.ContainsKey(mapping.ContentType2))
  246. content2Asset.Add(mapping.ContentType2, assetType);
  247. if (!content2Inventory.ContainsKey(mapping.ContentType2))
  248. content2Inventory.Add(mapping.ContentType2, mapping.InventoryType);
  249. }
  250. }
  251. }
  252. public static AssetType SLAssetName2Type(string name)
  253. {
  254. if (name2Asset.TryGetValue(name, out AssetType type))
  255. return type;
  256. return AssetType.Unknown;
  257. }
  258. public static FolderType SLInvName2Type(string name)
  259. {
  260. if (name2Inventory.TryGetValue(name, out FolderType type))
  261. return type;
  262. return FolderType.None;
  263. }
  264. public static string SLAssetTypeToContentType(int assetType)
  265. {
  266. string contentType;
  267. if (!asset2Content.TryGetValue((sbyte)assetType, out contentType))
  268. contentType = asset2Content[(sbyte)AssetType.Unknown];
  269. return contentType;
  270. }
  271. public static string SLInvTypeToContentType(int invType)
  272. {
  273. string contentType;
  274. if (!inventory2Content.TryGetValue((sbyte)invType, out contentType))
  275. contentType = inventory2Content[(sbyte)InventoryType.Unknown];
  276. return contentType;
  277. }
  278. public static sbyte ContentTypeToSLAssetType(string contentType)
  279. {
  280. sbyte assetType;
  281. if (!content2Asset.TryGetValue(contentType, out assetType))
  282. assetType = (sbyte)AssetType.Unknown;
  283. return (sbyte)assetType;
  284. }
  285. public static sbyte ContentTypeToSLInvType(string contentType)
  286. {
  287. sbyte invType;
  288. if (!content2Inventory.TryGetValue(contentType, out invType))
  289. invType = (sbyte)InventoryType.Unknown;
  290. return (sbyte)invType;
  291. }
  292. public static string SLAssetTypeToExtension(int assetType)
  293. {
  294. string extension;
  295. if (!asset2Extension.TryGetValue((sbyte)assetType, out extension))
  296. extension = asset2Extension[(sbyte)AssetType.Unknown];
  297. return extension;
  298. }
  299. #endregion SL / file extension / content-type conversions
  300. static char[] seps = new char[] { '\t', '\n' };
  301. static char[] stringseps = new char[] { '|', '\n' };
  302. static byte[] moronize = new byte[16]
  303. {
  304. 60, 17, 94, 81, 4, 244, 82, 60, 159, 166, 152, 175, 241, 3, 71, 48
  305. };
  306. static int getField(string note, int start, string name, bool isString, out string value)
  307. {
  308. value = String.Empty;
  309. int end = -1;
  310. int limit = note.Length - start;
  311. if (limit > 64)
  312. limit = 64;
  313. int indx = note.IndexOf(name, start, limit);
  314. if (indx < 0)
  315. return -1;
  316. indx += name.Length + 1; // eat \t
  317. limit = note.Length - indx - 2;
  318. if (limit > 129)
  319. limit = 129;
  320. if (isString)
  321. end = note.IndexOfAny(stringseps, indx, limit);
  322. else
  323. end = note.IndexOfAny(seps, indx, limit);
  324. if (end < 0)
  325. return -1;
  326. value = note.Substring(indx, end - indx);
  327. return end;
  328. }
  329. private static UUID deMoronize(UUID id)
  330. {
  331. byte[] data = new byte[16];
  332. id.ToBytes(data,0);
  333. for(int i = 0; i < 16; ++i)
  334. data[i] ^= moronize[i];
  335. return new UUID(data,0);
  336. }
  337. public static InventoryItemBase GetEmbeddedItem(byte[] data, UUID itemID)
  338. {
  339. if(data == null || data.Length < 300)
  340. return null;
  341. string note = Util.UTF8.GetString(data);
  342. if (String.IsNullOrWhiteSpace(note))
  343. return null;
  344. // waste some time checking rigid versions
  345. string version = note.Substring(0,21);
  346. if (!version.Equals("Linden text version 2"))
  347. return null;
  348. version = note.Substring(24, 25);
  349. if (!version.Equals("LLEmbeddedItems version 1"))
  350. return null;
  351. int indx = note.IndexOf(itemID.ToString(), 100);
  352. if (indx < 0)
  353. return null;
  354. indx = note.IndexOf("permissions", indx, 100); // skip parentID
  355. if (indx < 0)
  356. return null;
  357. string valuestr;
  358. indx = getField(note, indx, "base_mask", false, out valuestr);
  359. if (indx < 0)
  360. return null;
  361. if (!uint.TryParse(valuestr, NumberStyles.HexNumber, Culture.NumberFormatInfo, out uint basemask))
  362. return null;
  363. indx = getField(note, indx, "owner_mask", false, out valuestr);
  364. if (indx < 0)
  365. return null;
  366. if (!uint.TryParse(valuestr, NumberStyles.HexNumber, Culture.NumberFormatInfo, out uint ownermask))
  367. return null;
  368. indx = getField(note, indx, "group_mask", false, out valuestr);
  369. if (indx < 0)
  370. return null;
  371. if (!uint.TryParse(valuestr, NumberStyles.HexNumber, Culture.NumberFormatInfo, out uint groupmask))
  372. return null;
  373. indx = getField(note, indx, "everyone_mask", false, out valuestr);
  374. if (indx < 0)
  375. return null;
  376. if (!uint.TryParse(valuestr, NumberStyles.HexNumber, Culture.NumberFormatInfo, out uint everyonemask))
  377. return null;
  378. indx = getField(note, indx, "next_owner_mask", false, out valuestr);
  379. if (indx < 0)
  380. return null;
  381. if (!uint.TryParse(valuestr, NumberStyles.HexNumber, Culture.NumberFormatInfo, out uint nextownermask))
  382. return null;
  383. indx = getField(note, indx, "creator_id", false, out valuestr);
  384. if (indx < 0)
  385. return null;
  386. if (!UUID.TryParse(valuestr, out UUID creatorID))
  387. return null;
  388. indx = getField(note, indx, "owner_id", false, out valuestr);
  389. if (indx < 0)
  390. return null;
  391. if (!UUID.TryParse(valuestr, out UUID ownerID))
  392. return null;
  393. int limit = note.Length - indx;
  394. if (limit > 120)
  395. limit = 120;
  396. indx = note.IndexOf('}', indx, limit); // last owner
  397. if (indx < 0)
  398. return null;
  399. int curindx = indx;
  400. UUID assetID = UUID.Zero;
  401. indx = getField(note, indx, "asset_id", false, out valuestr);
  402. if (indx < 0)
  403. {
  404. indx = getField(note, curindx, "shadow_id", false, out valuestr);
  405. if (indx < 0)
  406. return null;
  407. if (!UUID.TryParse(valuestr, out assetID))
  408. return null;
  409. assetID = deMoronize(assetID);
  410. }
  411. else
  412. {
  413. if (!UUID.TryParse(valuestr, out assetID))
  414. return null;
  415. }
  416. indx = getField(note, indx, "type", false, out valuestr);
  417. if (indx < 0)
  418. return null;
  419. AssetType assetType = SLAssetName2Type(valuestr);
  420. indx = getField(note, indx, "inv_type", false, out valuestr);
  421. if (indx < 0)
  422. return null;
  423. FolderType invType = SLInvName2Type(valuestr);
  424. indx = getField(note, indx, "flags", false, out valuestr);
  425. if (indx < 0)
  426. return null;
  427. if (!uint.TryParse(valuestr, NumberStyles.HexNumber, Culture.NumberFormatInfo, out uint flags))
  428. return null;
  429. limit = note.Length - indx;
  430. if (limit > 120)
  431. limit = 120;
  432. indx = note.IndexOf('}', indx, limit); // skip sale
  433. if (indx < 0)
  434. return null;
  435. indx = getField(note, indx, "name", true, out valuestr);
  436. if (indx < 0)
  437. return null;
  438. string name = valuestr;
  439. indx = getField(note, indx, "desc", true, out valuestr);
  440. if (indx < 0)
  441. return null;
  442. string desc = valuestr;
  443. InventoryItemBase item = new InventoryItemBase();
  444. item.AssetID = assetID;
  445. item.AssetType = (sbyte)assetType;
  446. item.BasePermissions = basemask;
  447. item.CreationDate = Util.UnixTimeSinceEpoch();
  448. item.CreatorData = "";
  449. item.CreatorId = creatorID.ToString();
  450. item.CurrentPermissions = ownermask;
  451. item.Description = desc;
  452. item.Flags = flags;
  453. item.Folder = UUID.Zero;
  454. item.GroupID = UUID.Zero;
  455. item.GroupOwned = false;
  456. item.GroupPermissions = groupmask;
  457. item.InvType = (sbyte)invType;
  458. item.Name = name;
  459. item.NextPermissions = nextownermask;
  460. item.Owner = ownerID;
  461. item.SalePrice = 0;
  462. item.SaleType = (byte)SaleType.Not;
  463. item.ID = UUID.Random();
  464. return item;
  465. }
  466. public static List<UUID> GetEmbeddedAssetIDs(byte[] data)
  467. {
  468. if (data == null || data.Length < 79)
  469. return null;
  470. string note = Util.UTF8.GetString(data);
  471. if (String.IsNullOrWhiteSpace(note))
  472. return null;
  473. // waste some time checking rigid versions
  474. string tmpStr = note.Substring(0, 21);
  475. if (!tmpStr.Equals("Linden text version 2"))
  476. return null;
  477. tmpStr = note.Substring(24, 25);
  478. if (!tmpStr.Equals("LLEmbeddedItems version 1"))
  479. return null;
  480. tmpStr = note.Substring(52,5);
  481. if (!tmpStr.Equals("count"))
  482. return null;
  483. int limit = note.Length - 57 - 2;
  484. if (limit > 8)
  485. limit = 8;
  486. int indx = note.IndexOfAny(seps, 57, limit);
  487. if(indx < 0)
  488. return null;
  489. if (!int.TryParse(note.Substring(57, indx - 57), out int count))
  490. return null;
  491. List<UUID> ids = new List<UUID>();
  492. while(count > 0)
  493. {
  494. string valuestr;
  495. UUID assetID = UUID.Zero;
  496. indx = note.IndexOf('}',indx); // skip to end of permissions
  497. if (indx < 0)
  498. return null;
  499. int curindx = indx;
  500. indx = getField(note, indx, "asset_id", false, out valuestr);
  501. if (indx < 0)
  502. {
  503. indx = getField(note, curindx, "shadow_id", false, out valuestr);
  504. if (indx < 0)
  505. return null;
  506. if (!UUID.TryParse(valuestr, out assetID))
  507. return null;
  508. assetID = deMoronize(assetID);
  509. }
  510. else
  511. {
  512. if (!UUID.TryParse(valuestr, out assetID))
  513. return null;
  514. }
  515. ids.Add(assetID);
  516. indx = note.IndexOf('}', indx); // skip to end of sale
  517. if (indx < 0)
  518. return null;
  519. indx = getField(note, indx, "name", false, out valuestr); // avoid name contents
  520. if (indx < 0)
  521. return null;
  522. indx = getField(note, indx, "desc", false, out valuestr); // avoid desc contents
  523. if (indx < 0)
  524. return null;
  525. if(count > 1)
  526. {
  527. indx = note.IndexOf("ext char index", indx); // skip to next
  528. if (indx < 0)
  529. return null;
  530. }
  531. --count;
  532. }
  533. indx = note.IndexOf("Text length",indx);
  534. if(indx > 0)
  535. {
  536. indx += 14;
  537. List<UUID> textIDs = Util.GetUUIDsOnString(ref note, indx, note.Length - indx);
  538. if(textIDs.Count > 0)
  539. ids.AddRange(textIDs);
  540. }
  541. if (ids.Count == 0)
  542. return null;
  543. return ids;
  544. }
  545. /// <summary>
  546. /// Parse a notecard in Linden format to a list of ordinary lines for LSL
  547. /// </summary>
  548. /// <param name="rawInput"></param>
  549. /// <returns></returns>
  550. public static string[] ParseNotecardToArray(byte[] data)
  551. {
  552. // check of a valid notecard
  553. if (data == null || data.Length < 79)
  554. return new string[0];
  555. //LSL can't read notecards with embedded items
  556. if (data[58] != '0' || data[59] != '\n')
  557. return new string[0];
  558. string note = Util.UTF8.GetString(data);
  559. if (String.IsNullOrWhiteSpace(note))
  560. return new string[0];
  561. // waste some time checking rigid versions
  562. string tmpStr = note.Substring(0, 21);
  563. if (!tmpStr.Equals("Linden text version 2"))
  564. return new string[0];
  565. tmpStr = note.Substring(24, 25);
  566. if (!tmpStr.Equals("LLEmbeddedItems version 1"))
  567. return new string[0];
  568. tmpStr = note.Substring(52, 5);
  569. if (!tmpStr.Equals("count"))
  570. return new string[0];
  571. int indx = note.IndexOf("Text length", 60);
  572. if(indx < 0)
  573. return new string[0];
  574. indx += 12;
  575. int end = indx + 1;
  576. for (; end < note.Length && note[end] != '\n'; ++end);
  577. if (note[end] != '\n')
  578. return new string[0];
  579. tmpStr = note.Substring(indx, end - indx);
  580. if (!int.TryParse(tmpStr, out int textLen) || textLen == 0)
  581. return new string[0];
  582. indx = end + 1;
  583. if (textLen + indx > data.Length)
  584. return new string[0];
  585. // yeackk
  586. note = Util.UTF8.GetString(data, indx, textLen);
  587. textLen = note.Length;
  588. indx = 0;
  589. var lines = new List<string>();
  590. while (indx < textLen)
  591. {
  592. end = indx;
  593. for (; end < textLen && note[end] != '\n'; ++end);
  594. if(end == indx)
  595. lines.Add(String.Empty);
  596. else
  597. lines.Add(note.Substring(indx, end - indx));
  598. indx = end + 1;
  599. }
  600. // notes only seem to have one text section
  601. if(lines.Count == 0)
  602. return new string[0];
  603. return lines.ToArray();
  604. }
  605. // libomv has old names on ATTACH_LEFT_PEC and ATTACH_RIGHT_PEC
  606. public static readonly string[] AttachmentPointNames = new string[]
  607. {
  608. string.Empty,
  609. "ATTACH_CHEST", // 1
  610. "ATTACH_HEAD", // 2
  611. "ATTACH_LSHOULDER", // 3
  612. "ATTACH_RSHOULDER", // 4
  613. "ATTACH_LHAND", // 5
  614. "ATTACH_RHAND", // 6
  615. "ATTACH_LFOOT", // 7
  616. "ATTACH_RFOOT", // 8
  617. "ATTACH_BACK", // 9
  618. "ATTACH_PELVIS", // 10
  619. "ATTACH_MOUTH", // 11
  620. "ATTACH_CHIN", // 12
  621. "ATTACH_LEAR", // 13
  622. "ATTACH_REAR", // 14
  623. "ATTACH_LEYE", // 15
  624. "ATTACH_REYE", // 16
  625. "ATTACH_NOSE", // 17
  626. "ATTACH_RUARM", // 18
  627. "ATTACH_RLARM", // 19
  628. "ATTACH_LUARM", // 20
  629. "ATTACH_LLARM", // 21
  630. "ATTACH_RHIP", // 22
  631. "ATTACH_RULEG", // 23
  632. "ATTACH_RLLEG", // 24
  633. "ATTACH_LHIP", // 25
  634. "ATTACH_LULEG", // 26
  635. "ATTACH_LLLEG", // 27
  636. "ATTACH_BELLY", // 28
  637. "ATTACH_LEFT_PEC", // 29
  638. "ATTACH_RIGHT_PEC", // 30
  639. "ATTACH_HUD_CENTER_2", // 31
  640. "ATTACH_HUD_TOP_RIGHT", // 32
  641. "ATTACH_HUD_TOP_CENTER", // 33
  642. "ATTACH_HUD_TOP_LEFT", // 34
  643. "ATTACH_HUD_CENTER_1", // 35
  644. "ATTACH_HUD_BOTTOM_LEFT", // 36
  645. "ATTACH_HUD_BOTTOM", // 37
  646. "ATTACH_HUD_BOTTOM_RIGHT", // 38
  647. "ATTACH_NECK", // 39
  648. "ATTACH_AVATAR_CENTER", // 40
  649. "ATTACH_LHAND_RING1", // 41
  650. "ATTACH_RHAND_RING1", // 42
  651. "ATTACH_TAIL_BASE", // 43
  652. "ATTACH_TAIL_TIP", // 44
  653. "ATTACH_LWING", // 45
  654. "ATTACH_RWING", // 46
  655. "ATTACH_FACE_JAW", // 47
  656. "ATTACH_FACE_LEAR", // 48
  657. "ATTACH_FACE_REAR", // 49
  658. "ATTACH_FACE_LEYE", // 50
  659. "ATTACH_FACE_REYE", // 51
  660. "ATTACH_FACE_TONGUE", // 52
  661. "ATTACH_GROIN", // 53
  662. "ATTACH_HIND_LFOOT", // 54
  663. "ATTACH_HIND_RFOOT" // 55
  664. };
  665. public static string GetAttachmentName(int point)
  666. {
  667. if(point < AttachmentPointNames.Length)
  668. return AttachmentPointNames[point];
  669. return "Unknown";
  670. }
  671. }
  672. }