123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using ICSharpCode.SharpZipLib.Zip;
- using TriLibCore.General;
- using TriLibCore.Mappers;
- using TriLibCore.Utils;
- using UnityEngine;
- using Object = UnityEngine.Object;
- namespace TriLibCore
- {
- /// <summary>Represents an Asset Loader class which works with Zip files.</summary>
- //todo: unify async loading.
- public static class AssetLoaderZip
- {
- /// <summary>
- /// Buffer size used to copy the zip file entries content.
- /// </summary>
- private const int ZipBufferSize = 4096;
- /// <summary>Loads a model from the given Zip file path asynchronously.</summary>
- /// <param name="path">The Zip file path.</param>
- /// <param name="onLoad">The Method to call on the Main Thread when the Model Meshes and hierarchy are loaded.</param>
- /// <param name="onMaterialsLoad">The Method to call on the Main Thread when the Model (including Textures and Materials) has been fully loaded.</param>
- /// <param name="onProgress">The Method to call when the Model loading progress changes.</param>
- /// <param name="onError">The Method to call on the Main Thread when any error occurs.</param>
- /// <param name="wrapperGameObject">The Game Object that will be the parent of the loaded Game Object. Can be null.</param>
- /// <param name="assetLoaderOptions">The Asset Loader Options reference. Asset Loader Options contains various options used during the Model loading process.</param>
- /// <param name="customContextData">The Custom Data that will be passed along the AssetLoaderContext.</param>
- /// <param name="fileExtension">The Model inside the Zip file extension. If <c>null</c> TriLib will try to find a suitable model format inside the Zip file.</param>
- /// <returns>The asset loader context, containing model loading information and the output game object.</returns>
- public static AssetLoaderContext LoadModelFromZipFile(string path, Action<AssetLoaderContext> onLoad, Action<AssetLoaderContext> onMaterialsLoad, Action<AssetLoaderContext, float> onProgress, Action<IContextualizedError> onError = null, GameObject wrapperGameObject = null, AssetLoaderOptions assetLoaderOptions = null, object customContextData = null, string fileExtension = null)
- {
- Stream stream = null;
- var memoryStream = SetupZipModelLoading(ref stream, path, onError, assetLoaderOptions, ref fileExtension, out var zipFile);
- return AssetLoader.LoadModelFromStream(memoryStream, path, fileExtension, onLoad, delegate (AssetLoaderContext assetLoaderContext)
- {
- var zipLoadCustomContextData = assetLoaderContext.CustomData as ZipLoadCustomContextData;
- zipLoadCustomContextData?.Stream.Close();
- onMaterialsLoad?.Invoke(assetLoaderContext);
- }, onProgress, OnError, wrapperGameObject, assetLoaderOptions, new ZipLoadCustomContextData
- {
- ZipFile = zipFile,
- Stream = stream,
- CustomData = customContextData
- });
- }
- /// <summary>Loads a model from the given Zip file Stream asynchronously.</summary>
- /// <param name="stream">The Stream containing the Zip data.</param>
- /// <param name="onLoad">The Method to call on the Main Thread when the Model Meshes and hierarchy are loaded.</param>
- /// <param name="onMaterialsLoad">The Method to call on the Main Thread when the Model (including Textures and Materials) has been fully loaded.</param>
- /// <param name="onProgress">The Method to call when the Model loading progress changes.</param>
- /// <param name="onError">The Method to call on the Main Thread when any error occurs.</param>
- /// <param name="wrapperGameObject">The Game Object that will be the parent of the loaded Game Object. Can be null.</param>
- /// <param name="assetLoaderOptions">The Asset Loader Options reference. Asset Loader Options contains various options used during the Model loading process.</param>
- /// <param name="customContextData">The Custom Data that will be passed along the AssetLoaderContext.</param>
- /// <param name="fileExtension">The Model inside the Zip file extension. If <c>null</c> TriLib will try to find a suitable model format inside the Zip file.</param>
- /// <returns>The asset loader context, containing model loading information and the output game object.</returns>
- public static AssetLoaderContext LoadModelFromZipStream(Stream stream, Action<AssetLoaderContext> onLoad, Action<AssetLoaderContext> onMaterialsLoad, Action<AssetLoaderContext, float> onProgress, Action<IContextualizedError> onError = null, GameObject wrapperGameObject = null, AssetLoaderOptions assetLoaderOptions = null, object customContextData = null, string fileExtension = null)
- {
- var memoryStream = SetupZipModelLoading(ref stream, null, onError, assetLoaderOptions, ref fileExtension, out var zipFile);
- return AssetLoader.LoadModelFromStream(memoryStream, null, fileExtension, onLoad, delegate (AssetLoaderContext assetLoaderContext)
- {
- var zipLoadCustomContextData = assetLoaderContext.CustomData as ZipLoadCustomContextData;
- zipLoadCustomContextData?.Stream.Close();
- onMaterialsLoad?.Invoke(assetLoaderContext);
- }, onProgress, OnError, wrapperGameObject, assetLoaderOptions, new ZipLoadCustomContextData
- {
- ZipFile = zipFile,
- Stream = stream,
- CustomData = customContextData
- });
- }
- /// <summary>Loads a model from the given Zip file path synchronously.</summary>
- /// <param name="path">The Zip file path.</param>
- /// <param name="onError">The Method to call on the Main Thread when any error occurs.</param>
- /// <param name="wrapperGameObject">The Game Object that will be the parent of the loaded Game Object. Can be null.</param>
- /// <param name="assetLoaderOptions">The Asset Loader Options reference. Asset Loader Options contains various options used during the Model loading process.</param>
- /// <param name="customContextData">The Custom Data that will be passed along the AssetLoaderContext.</param>
- /// <param name="fileExtension">The Model inside the Zip file extension. If <c>null</c> TriLib will try to find a suitable model format inside the Zip file.</param>
- /// <returns>The asset loader context, containing model loading information and the output game object.</returns>
- public static AssetLoaderContext LoadModelFromZipFileNoThread(string path, Action<IContextualizedError> onError = null, GameObject wrapperGameObject = null, AssetLoaderOptions assetLoaderOptions = null, object customContextData = null, string fileExtension = null)
- {
- Stream stream = null;
- var memoryStream = SetupZipModelLoading(ref stream, path, onError, assetLoaderOptions, ref fileExtension, out var zipFile);
- var assetLoaderContext = AssetLoader.LoadModelFromStreamNoThread(memoryStream, path, fileExtension, OnError, wrapperGameObject, assetLoaderOptions, new ZipLoadCustomContextData
- {
- ZipFile = zipFile,
- CustomData = customContextData
- });
- stream.Close();
- return assetLoaderContext;
- }
- /// <summary>Loads a model from the given Zip file Stream synchronously.</summary>
- /// <param name="stream">The Stream containing the Zip data.</param>
- /// <param name="onError">The Method to call on the Main Thread when any error occurs.</param>
- /// <param name="wrapperGameObject">The Game Object that will be the parent of the loaded Game Object. Can be null.</param>
- /// <param name="assetLoaderOptions">The Asset Loader Options reference. Asset Loader Options contains various options used during the Model loading process.</param>
- /// <param name="customContextData">The Custom Data that will be passed along the AssetLoaderContext.</param>
- /// <param name="fileExtension">The Model inside the Zip file extension. If <c>null</c> TriLib will try to find a suitable model format inside the Zip file.</param>
- /// <returns>The asset loader context, containing model loading information and the output game object.</returns>
- public static AssetLoaderContext LoadModelFromZipStreamNoThread(Stream stream, Action<IContextualizedError> onError, GameObject wrapperGameObject = null, AssetLoaderOptions assetLoaderOptions = null, object customContextData = null, string fileExtension = null)
- {
- var memoryStream = SetupZipModelLoading(ref stream, null, onError, assetLoaderOptions, ref fileExtension, out var zipFile);
- var assetLoaderContext = AssetLoader.LoadModelFromStreamNoThread(memoryStream, null, fileExtension, OnError, wrapperGameObject, assetLoaderOptions, new ZipLoadCustomContextData
- {
- ZipFile = zipFile,
- CustomData = customContextData
- });
- stream.Close();
- return assetLoaderContext;
- }
- /// <summary>
- /// Called when any loading error occurs.
- /// </summary>
- /// <param name="contextualizedError">Thrown error containing an attached context.</param>
- private static void OnError(IContextualizedError contextualizedError)
- {
- var assetLoaderContext = contextualizedError?.GetContext() as AssetLoaderContext;
- var zipLoadCustomContextData = assetLoaderContext?.CustomData as ZipLoadCustomContextData;
- zipLoadCustomContextData?.Stream.Close();
- assetLoaderContext?.OnError?.Invoke(contextualizedError);
- }
- /// <summary>Configures the Zip Model loading, adding the Zip External Data/Texture Mappers.</summary>
- /// <param name="stream">The Stream containing the Zip data.</param>
- /// <param name="path">The Zip file path.</param>
- /// <param name="onError">The Method to call on the Main Thread when any error occurs.</param>
- /// <param name="assetLoaderOptions">The Asset Loader Options reference. Asset Loader Options contains various options used during the Model loading process.</param>
- /// <param name="fileExtension">The Model inside the Zip file extension.</param>
- /// <param name="zipFile">The Zip file instance.</param>
- /// <returns>The model file data stream.</returns>
- private static Stream SetupZipModelLoading(ref Stream stream, string path, Action<IContextualizedError> onError, AssetLoaderOptions assetLoaderOptions, ref string fileExtension, out ZipFile zipFile)
- {
- if (assetLoaderOptions == null)
- {
- assetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions();
- }
- assetLoaderOptions.TextureMapper = ScriptableObject.CreateInstance<ZipFileTextureMapper>();
- assetLoaderOptions.ExternalDataMapper = ScriptableObject.CreateInstance<ZipFileExternalDataMapper>();
- assetLoaderOptions.FixedAllocations.Add(assetLoaderOptions.TextureMapper);
- assetLoaderOptions.FixedAllocations.Add(assetLoaderOptions.ExternalDataMapper);
- if (stream == null)
- {
- stream = new FileStream(path, FileMode.Open);
- }
- var validExtensions = Readers.Extensions;
- zipFile = new ZipFile(stream);
- Stream memoryStream = null;
- foreach (ZipEntry zipEntry in zipFile)
- {
- if (!zipEntry.IsFile)
- {
- continue;
- }
- var checkingFileExtension = FileUtils.GetFileExtension(zipEntry.Name, false);
- if (fileExtension != null && checkingFileExtension == fileExtension)
- {
- memoryStream = ZipFileEntryToStream(out fileExtension, zipEntry, zipFile);
- }
- else if (validExtensions.Contains(checkingFileExtension))
- {
- memoryStream = ZipFileEntryToStream(out fileExtension, zipEntry, zipFile);
- break;
- }
- }
- if (memoryStream == null)
- {
- var exception = new Exception("Unable to find a suitable model on the Zip file. Please inform a valid model file extension.");
- if (onError != null)
- {
- var error = exception as IContextualizedError;
- onError(error ?? new ContextualizedError<AssetLoaderOptions>(exception, assetLoaderOptions));
- }
- else
- {
- throw exception;
- }
- }
- return memoryStream;
- }
- /// <summary>Copies the contents of a Zip file Entry into a Memory Stream.</summary>
- /// <param name="fileExtension">The Model inside the Zip file extension</param>
- /// <param name="zipEntry">The Zip file Entry.</param>
- /// <param name="zipFile">The Zip file instance.</param>
- /// <returns>A memory stream with the zip file entry contents.</returns>
- public static Stream ZipFileEntryToStream(out string fileExtension, ZipEntry zipEntry, ZipFile zipFile)
- {
- var buffer = new byte[ZipBufferSize];
- var zipFileStream = zipFile.GetInputStream(zipEntry);
- var memoryStream = new MemoryStream(ZipBufferSize);
- ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(zipFileStream, memoryStream, buffer);
- memoryStream.Seek(0, SeekOrigin.Begin);
- zipFileStream.Close();
- fileExtension = FileUtils.GetFileExtension(zipEntry.Name, false);
- return memoryStream;
- }
- }
- }
|