clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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  
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.IO.Compression;
32 using System.Reflection;
33 using System.Threading;
34 using System.Text;
35 using System.Xml;
36 using System.Xml.Linq;
37 using log4net;
38 using OpenMetaverse;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Serialization;
41 using OpenSim.Framework.Serialization.External;
42 using OpenSim.Region.CoreModules.World.Archiver;
43 using OpenSim.Region.Framework.Scenes;
44 using OpenSim.Region.Framework.Scenes.Serialization;
45 using OpenSim.Region.Framework.Interfaces;
46 using OpenSim.Services.Interfaces;
47  
48 namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
49 {
50 public class InventoryArchiveReadRequest
51 {
52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53  
54 /// <summary>
55 /// The maximum major version of archive that we can read. Minor versions shouldn't need a max number since version
56 /// bumps here should be compatible.
57 /// </summary>
58 public static int MAX_MAJOR_VERSION = 1;
59  
60 protected TarArchiveReader archive;
61  
62 private UserAccount m_userInfo;
63 private string m_invPath;
64  
65 /// <value>
66 /// ID of this request
67 /// </value>
68 protected UUID m_id;
69  
70 /// <summary>
71 /// Do we want to merge this load with existing inventory?
72 /// </summary>
73 protected bool m_merge;
74  
75 protected IInventoryService m_InventoryService;
76 protected IAssetService m_AssetService;
77 protected IUserAccountService m_UserAccountService;
78  
79 private InventoryArchiverModule m_module;
80  
81 /// <value>
82 /// The stream from which the inventory archive will be loaded.
83 /// </value>
84 private Stream m_loadStream;
85  
86 /// <summary>
87 /// Has the control file been loaded for this archive?
88 /// </summary>
89 public bool ControlFileLoaded { get; private set; }
90  
91 /// <summary>
92 /// Do we want to enforce the check. IAR versions before 0.2 and 1.1 do not guarantee this order, so we can't
93 /// enforce.
94 /// </summary>
95 public bool EnforceControlFileCheck { get; private set; }
96  
97 protected bool m_assetsLoaded;
98 protected bool m_inventoryNodesLoaded;
99  
100 protected int m_successfulAssetRestores;
101 protected int m_failedAssetRestores;
102 protected int m_successfulItemRestores;
103  
104 /// <summary>
105 /// Root destination folder for the IAR load.
106 /// </summary>
107 protected InventoryFolderBase m_rootDestinationFolder;
108  
109 /// <summary>
110 /// Inventory nodes loaded from the iar.
111 /// </summary>
112 protected HashSet<InventoryNodeBase> m_loadedNodes = new HashSet<InventoryNodeBase>();
113  
114 /// <summary>
115 /// In order to load identically named folders, we need to keep track of the folders that we have already
116 /// resolved.
117 /// </summary>
118 Dictionary <string, InventoryFolderBase> m_resolvedFolders = new Dictionary<string, InventoryFolderBase>();
119  
120 /// <summary>
121 /// Record the creator id that should be associated with an asset. This is used to adjust asset creator ids
122 /// after OSP resolution (since OSP creators are only stored in the item
123 /// </summary>
124 protected Dictionary<UUID, UUID> m_creatorIdForAssetId = new Dictionary<UUID, UUID>();
125  
126 public InventoryArchiveReadRequest(
127 UUID id, IInventoryService inv, InventoryArchiverModule module, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, string loadPath, bool merge)
128 : this(
129 id,
130 inv,
131 module,
132 assets,
133 uacc,
134 userInfo,
135 invPath,
136 new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress),
137 merge)
138 {
139 }
140  
141 public InventoryArchiveReadRequest(
142 UUID id, IInventoryService inv, InventoryArchiverModule module, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, Stream loadStream, bool merge)
143 {
144 m_id = id;
145 m_InventoryService = inv;
146 m_AssetService = assets;
147 m_UserAccountService = uacc;
148 m_merge = merge;
149 m_userInfo = userInfo;
150 m_invPath = invPath;
151 m_loadStream = loadStream;
152 m_module = module;
153  
154 // FIXME: Do not perform this check since older versions of OpenSim do save the control file after other things
155 // (I thought they weren't). We will need to bump the version number and perform this check on all
156 // subsequent IAR versions only
157 ControlFileLoaded = true;
158 }
159  
160 /// <summary>
161 /// Execute the request
162 /// </summary>
163 /// <remarks>
164 /// Only call this once. To load another IAR, construct another request object.
165 /// </remarks>
166 /// <returns>
167 /// A list of the inventory nodes loaded. If folders were loaded then only the root folders are
168 /// returned
169 /// </returns>
170 /// <exception cref="System.Exception">Thrown if load fails.</exception>
171 public HashSet<InventoryNodeBase> Execute()
172 {
173 try
174 {
175 Exception reportedException = null;
176  
177 string filePath = "ERROR";
178  
179 List<InventoryFolderBase> folderCandidates
180 = InventoryArchiveUtils.FindFoldersByPath(
181 m_InventoryService, m_userInfo.PrincipalID, m_invPath);
182  
183 if (folderCandidates.Count == 0)
184 {
185 // Possibly provide an option later on to automatically create this folder if it does not exist
186 m_log.ErrorFormat("[INVENTORY ARCHIVER]: Inventory path {0} does not exist", m_invPath);
187  
188 return m_loadedNodes;
189 }
190  
191 m_rootDestinationFolder = folderCandidates[0];
192 archive = new TarArchiveReader(m_loadStream);
193 byte[] data;
194 TarArchiveReader.TarEntryType entryType;
195  
196 while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
197 {
198 if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
199 {
200 LoadControlFile(filePath, data);
201 }
202 else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
203 {
204 LoadAssetFile(filePath, data);
205 }
206 else if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH))
207 {
208 LoadInventoryFile(filePath, entryType, data);
209 }
210 }
211  
212 archive.Close();
213  
214 m_log.DebugFormat(
215 "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures",
216 m_successfulAssetRestores, m_failedAssetRestores);
217  
218 //Alicia: When this is called by LibraryModule or Tests, m_module will be null as event is not required
219 if(m_module != null)
220 m_module.TriggerInventoryArchiveLoaded(m_id, true, m_userInfo, m_invPath, m_loadStream, reportedException, m_successfulItemRestores);
221  
222 return m_loadedNodes;
223 }
224 catch(Exception Ex)
225 {
226 // Trigger saved event with failed result and exception data
227 if (m_module != null)
228 m_module.TriggerInventoryArchiveLoaded(m_id, false, m_userInfo, m_invPath, m_loadStream, Ex, 0);
229  
230 return m_loadedNodes;
231 }
232 finally
233 {
234 m_loadStream.Close();
235 }
236 }
237  
238 public void Close()
239 {
240 if (m_loadStream != null)
241 m_loadStream.Close();
242 }
243  
244 /// <summary>
245 /// Replicate the inventory paths in the archive to the user's inventory as necessary.
246 /// </summary>
247 /// <param name="iarPath">The item archive path to replicate</param>
248 /// <param name="rootDestinationFolder">The root folder for the inventory load</param>
249 /// <param name="resolvedFolders">
250 /// The folders that we have resolved so far for a given archive path.
251 /// This method will add more folders if necessary
252 /// </param>
253 /// <param name="loadedNodes">
254 /// Track the inventory nodes created.
255 /// </param>
256 /// <returns>The last user inventory folder created or found for the archive path</returns>
257 public InventoryFolderBase ReplicateArchivePathToUserInventory(
258 string iarPath,
259 InventoryFolderBase rootDestFolder,
260 Dictionary <string, InventoryFolderBase> resolvedFolders,
261 HashSet<InventoryNodeBase> loadedNodes)
262 {
263 string iarPathExisting = iarPath;
264  
265 // m_log.DebugFormat(
266 // "[INVENTORY ARCHIVER]: Loading folder {0} {1}", rootDestFolder.Name, rootDestFolder.ID);
267  
268 InventoryFolderBase destFolder
269 = ResolveDestinationFolder(rootDestFolder, ref iarPathExisting, resolvedFolders);
270  
271 // m_log.DebugFormat(
272 // "[INVENTORY ARCHIVER]: originalArchivePath [{0}], section already loaded [{1}]",
273 // iarPath, iarPathExisting);
274  
275 string iarPathToCreate = iarPath.Substring(iarPathExisting.Length);
276 CreateFoldersForPath(destFolder, iarPathExisting, iarPathToCreate, resolvedFolders, loadedNodes);
277  
278 return destFolder;
279 }
280  
281 /// <summary>
282 /// Resolve a destination folder
283 /// </summary>
284 ///
285 /// We require here a root destination folder (usually the root of the user's inventory) and the archive
286 /// path. We also pass in a list of previously resolved folders in case we've found this one previously.
287 ///
288 /// <param name="archivePath">
289 /// The item archive path to resolve. The portion of the path passed back is that
290 /// which corresponds to the resolved desintation folder.
291 /// <param name="rootDestinationFolder">
292 /// The root folder for the inventory load
293 /// </param>
294 /// <param name="resolvedFolders">
295 /// The folders that we have resolved so far for a given archive path.
296 /// </param>
297 /// <returns>
298 /// The folder in the user's inventory that matches best the archive path given. If no such folder was found
299 /// then the passed in root destination folder is returned.
300 /// </returns>
301 protected InventoryFolderBase ResolveDestinationFolder(
302 InventoryFolderBase rootDestFolder,
303 ref string archivePath,
304 Dictionary <string, InventoryFolderBase> resolvedFolders)
305 {
306 // string originalArchivePath = archivePath;
307  
308 while (archivePath.Length > 0)
309 {
310 // m_log.DebugFormat("[INVENTORY ARCHIVER]: Trying to resolve destination folder {0}", archivePath);
311  
312 if (resolvedFolders.ContainsKey(archivePath))
313 {
314 // m_log.DebugFormat(
315 // "[INVENTORY ARCHIVER]: Found previously created folder from archive path {0}", archivePath);
316 return resolvedFolders[archivePath];
317 }
318 else
319 {
320 if (m_merge)
321 {
322 // TODO: Using m_invPath is totally wrong - what we need to do is strip the uuid from the
323 // iar name and try to find that instead.
324 string plainPath = ArchiveConstants.ExtractPlainPathFromIarPath(archivePath);
325 List<InventoryFolderBase> folderCandidates
326 = InventoryArchiveUtils.FindFoldersByPath(
327 m_InventoryService, m_userInfo.PrincipalID, plainPath);
328  
329 if (folderCandidates.Count != 0)
330 {
331 InventoryFolderBase destFolder = folderCandidates[0];
332 resolvedFolders[archivePath] = destFolder;
333 return destFolder;
334 }
335 }
336  
337 // Don't include the last slash so find the penultimate one
338 int penultimateSlashIndex = archivePath.LastIndexOf("/", archivePath.Length - 2);
339  
340 if (penultimateSlashIndex >= 0)
341 {
342 // Remove the last section of path so that we can see if we've already resolved the parent
343 archivePath = archivePath.Remove(penultimateSlashIndex + 1);
344 }
345 else
346 {
347 // m_log.DebugFormat(
348 // "[INVENTORY ARCHIVER]: Found no previously created folder for archive path {0}",
349 // originalArchivePath);
350 archivePath = string.Empty;
351 return rootDestFolder;
352 }
353 }
354 }
355  
356 return rootDestFolder;
357 }
358  
359 /// <summary>
360 /// Create a set of folders for the given path.
361 /// </summary>
362 /// <param name="destFolder">
363 /// The root folder from which the creation will take place.
364 /// </param>
365 /// <param name="iarPathExisting">
366 /// the part of the iar path that already exists
367 /// </param>
368 /// <param name="iarPathToReplicate">
369 /// The path to replicate in the user's inventory from iar
370 /// </param>
371 /// <param name="resolvedFolders">
372 /// The folders that we have resolved so far for a given archive path.
373 /// </param>
374 /// <param name="loadedNodes">
375 /// Track the inventory nodes created.
376 /// </param>
377 protected void CreateFoldersForPath(
378 InventoryFolderBase destFolder,
379 string iarPathExisting,
380 string iarPathToReplicate,
381 Dictionary <string, InventoryFolderBase> resolvedFolders,
382 HashSet<InventoryNodeBase> loadedNodes)
383 {
384 string[] rawDirsToCreate = iarPathToReplicate.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
385  
386 for (int i = 0; i < rawDirsToCreate.Length; i++)
387 {
388 // m_log.DebugFormat("[INVENTORY ARCHIVER]: Creating folder {0} from IAR", rawDirsToCreate[i]);
389  
390 if (!rawDirsToCreate[i].Contains(ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR))
391 continue;
392  
393 int identicalNameIdentifierIndex
394 = rawDirsToCreate[i].LastIndexOf(
395 ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR);
396  
397 string newFolderName = rawDirsToCreate[i].Remove(identicalNameIdentifierIndex);
398  
399 newFolderName = InventoryArchiveUtils.UnescapeArchivePath(newFolderName);
400 UUID newFolderId = UUID.Random();
401  
402 // Asset type has to be Unknown here rather than Folder, otherwise the created folder can't be
403 // deleted once the client has relogged.
404 // The root folder appears to be labelled AssetType.Folder (shows up as "Category" in the client)
405 // even though there is a AssetType.RootCategory
406 destFolder
407 = new InventoryFolderBase(
408 newFolderId, newFolderName, m_userInfo.PrincipalID,
409 (short)AssetType.Unknown, destFolder.ID, 1);
410 m_InventoryService.AddFolder(destFolder);
411  
412 // Record that we have now created this folder
413 iarPathExisting += rawDirsToCreate[i] + "/";
414 m_log.DebugFormat("[INVENTORY ARCHIVER]: Created folder {0} from IAR", iarPathExisting);
415 resolvedFolders[iarPathExisting] = destFolder;
416  
417 if (0 == i)
418 loadedNodes.Add(destFolder);
419 }
420 }
421  
422 /// <summary>
423 /// Load an item from the archive
424 /// </summary>
425 /// <param name="filePath">The archive path for the item</param>
426 /// <param name="data">The raw item data</param>
427 /// <param name="rootDestinationFolder">The root destination folder for loaded items</param>
428 /// <param name="nodesLoaded">All the inventory nodes (items and folders) loaded so far</param>
429 protected InventoryItemBase LoadItem(byte[] data, InventoryFolderBase loadFolder)
430 {
431 InventoryItemBase item = UserInventoryItemSerializer.Deserialize(data);
432  
433 // Don't use the item ID that's in the file
434 item.ID = UUID.Random();
435  
436 UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_UserAccountService);
437 if (UUID.Zero != ospResolvedId) // The user exists in this grid
438 {
439 // m_log.DebugFormat("[INVENTORY ARCHIVER]: Found creator {0} via OSPA resolution", ospResolvedId);
440  
441 // item.CreatorIdAsUuid = ospResolvedId;
442  
443 // Don't preserve the OSPA in the creator id (which actually gets persisted to the
444 // database). Instead, replace with the UUID that we found.
445 item.CreatorId = ospResolvedId.ToString();
446 item.CreatorData = string.Empty;
447 }
448 else if (string.IsNullOrEmpty(item.CreatorData))
449 {
450 item.CreatorId = m_userInfo.PrincipalID.ToString();
451 // item.CreatorIdAsUuid = new UUID(item.CreatorId);
452 }
453  
454 item.Owner = m_userInfo.PrincipalID;
455  
456 // Reset folder ID to the one in which we want to load it
457 item.Folder = loadFolder.ID;
458  
459 // Record the creator id for the item's asset so that we can use it later, if necessary, when the asset
460 // is loaded.
461 // FIXME: This relies on the items coming before the assets in the TAR file. Need to create stronger
462 // checks for this, and maybe even an external tool for creating OARs which enforces this, rather than
463 // relying on native tar tools.
464 m_creatorIdForAssetId[item.AssetID] = item.CreatorIdAsUuid;
465  
466 if (!m_InventoryService.AddItem(item))
467 m_log.WarnFormat("[INVENTORY ARCHIVER]: Unable to save item {0} in folder {1}", item.Name, item.Folder);
468  
469 return item;
470 }
471  
472 /// <summary>
473 /// Load an asset
474 /// </summary>
475 /// <param name="assetFilename"></param>
476 /// <param name="data"></param>
477 /// <returns>true if asset was successfully loaded, false otherwise</returns>
478 private bool LoadAsset(string assetPath, byte[] data)
479 {
480 //IRegionSerialiser serialiser = scene.RequestModuleInterface<IRegionSerialiser>();
481 // Right now we're nastily obtaining the UUID from the filename
482 string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
483 int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
484  
485 if (i == -1)
486 {
487 m_log.ErrorFormat(
488 "[INVENTORY ARCHIVER]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping",
489 assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
490  
491 return false;
492 }
493  
494 string extension = filename.Substring(i);
495 string rawUuid = filename.Remove(filename.Length - extension.Length);
496 UUID assetId = new UUID(rawUuid);
497  
498 if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension))
499 {
500 sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension];
501  
502 if (assetType == (sbyte)AssetType.Unknown)
503 {
504 m_log.WarnFormat("[INVENTORY ARCHIVER]: Importing {0} byte asset {1} with unknown type", data.Length, assetId);
505 }
506 else if (assetType == (sbyte)AssetType.Object)
507 {
508 if (m_creatorIdForAssetId.ContainsKey(assetId))
509 {
510 data = SceneObjectSerializer.ModifySerializedObject(assetId, data,
511 sog => {
512 bool modified = false;
513  
514 foreach (SceneObjectPart sop in sog.Parts)
515 {
516 if (string.IsNullOrEmpty(sop.CreatorData))
517 {
518 sop.CreatorID = m_creatorIdForAssetId[assetId];
519 modified = true;
520 }
521 }
522  
523 return modified;
524 });
525  
526 if (data == null)
527 return false;
528 }
529 }
530  
531 //m_log.DebugFormat("[INVENTORY ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType);
532  
533 AssetBase asset = new AssetBase(assetId, "From IAR", assetType, UUID.Zero.ToString());
534 asset.Data = data;
535  
536 m_AssetService.Store(asset);
537  
538 return true;
539 }
540 else
541 {
542 m_log.ErrorFormat(
543 "[INVENTORY ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}",
544 assetPath, extension);
545  
546 return false;
547 }
548 }
549  
550 /// <summary>
551 /// Load control file
552 /// </summary>
553 /// <param name="path"></param>
554 /// <param name="data"></param>
555 public void LoadControlFile(string path, byte[] data)
556 {
557 XDocument doc = XDocument.Parse(Encoding.ASCII.GetString(data));
558 XElement archiveElement = doc.Element("archive");
559 int majorVersion = int.Parse(archiveElement.Attribute("major_version").Value);
560 int minorVersion = int.Parse(archiveElement.Attribute("minor_version").Value);
561 string version = string.Format("{0}.{1}", majorVersion, minorVersion);
562  
563 if (majorVersion > MAX_MAJOR_VERSION)
564 {
565 throw new Exception(
566 string.Format(
567 "The IAR you are trying to load has major version number of {0} but this version of OpenSim can only load IARs with major version number {1} and below",
568 majorVersion, MAX_MAJOR_VERSION));
569 }
570  
571 ControlFileLoaded = true;
572 m_log.InfoFormat("[INVENTORY ARCHIVER]: Loading IAR with version {0}", version);
573 }
574  
575 /// <summary>
576 /// Load inventory file
577 /// </summary>
578 /// <param name="path"></param>
579 /// <param name="entryType"></param>
580 /// <param name="data"></param>
581 protected void LoadInventoryFile(string path, TarArchiveReader.TarEntryType entryType, byte[] data)
582 {
583 if (!ControlFileLoaded)
584 throw new Exception(
585 string.Format(
586 "The IAR you are trying to load does not list {0} before {1}. Aborting load",
587 ArchiveConstants.CONTROL_FILE_PATH, ArchiveConstants.INVENTORY_PATH));
588  
589 if (m_assetsLoaded)
590 throw new Exception(
591 string.Format(
592 "The IAR you are trying to load does not list all {0} before {1}. Aborting load",
593 ArchiveConstants.INVENTORY_PATH, ArchiveConstants.ASSETS_PATH));
594  
595 path = path.Substring(ArchiveConstants.INVENTORY_PATH.Length);
596  
597 // Trim off the file portion if we aren't already dealing with a directory path
598 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY != entryType)
599 path = path.Remove(path.LastIndexOf("/") + 1);
600  
601 InventoryFolderBase foundFolder
602 = ReplicateArchivePathToUserInventory(
603 path, m_rootDestinationFolder, m_resolvedFolders, m_loadedNodes);
604  
605 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY != entryType)
606 {
607 InventoryItemBase item = LoadItem(data, foundFolder);
608  
609 if (item != null)
610 {
611 m_successfulItemRestores++;
612  
613 // If we aren't loading the folder containing the item then well need to update the
614 // viewer separately for that item.
615 if (!m_loadedNodes.Contains(foundFolder))
616 m_loadedNodes.Add(item);
617 }
618 }
619  
620 m_inventoryNodesLoaded = true;
621 }
622  
623 /// <summary>
624 /// Load asset file
625 /// </summary>
626 /// <param name="path"></param>
627 /// <param name="data"></param>
628 protected void LoadAssetFile(string path, byte[] data)
629 {
630 if (!ControlFileLoaded)
631 throw new Exception(
632 string.Format(
633 "The IAR you are trying to load does not list {0} before {1}. Aborting load",
634 ArchiveConstants.CONTROL_FILE_PATH, ArchiveConstants.ASSETS_PATH));
635  
636 if (!m_inventoryNodesLoaded)
637 throw new Exception(
638 string.Format(
639 "The IAR you are trying to load does not list all {0} before {1}. Aborting load",
640 ArchiveConstants.INVENTORY_PATH, ArchiveConstants.ASSETS_PATH));
641  
642 if (LoadAsset(path, data))
643 m_successfulAssetRestores++;
644 else
645 m_failedAssetRestores++;
646  
647 if ((m_successfulAssetRestores) % 50 == 0)
648 m_log.DebugFormat(
649 "[INVENTORY ARCHIVER]: Loaded {0} assets...",
650 m_successfulAssetRestores);
651  
652 m_assetsLoaded = true;
653 }
654 }
655 }