summaryrefslogtreecommitdiff
path: root/Runtime/Misc/AssetBundleUtility.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Misc/AssetBundleUtility.cpp')
-rw-r--r--Runtime/Misc/AssetBundleUtility.cpp692
1 files changed, 692 insertions, 0 deletions
diff --git a/Runtime/Misc/AssetBundleUtility.cpp b/Runtime/Misc/AssetBundleUtility.cpp
new file mode 100644
index 0000000..4c5b851
--- /dev/null
+++ b/Runtime/Misc/AssetBundleUtility.cpp
@@ -0,0 +1,692 @@
+#include "UnityPrefix.h"
+#include "AssetBundleUtility.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+#if ENABLE_WWW
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "PlatformDependent/CommonWebPlugin/FileStream.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Misc/WWWCached.h"
+#endif
+
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Runtime/Serialize/BuildTargetVerification.h"
+#endif
+
+static char const* kIncompatibleScriptsMsg = "The asset bundle '%s' could not be loaded because it references scripts that are not compatible with the currently loaded ones. Rebuild the AssetBundle to fix this error.";
+static char const* kIncompatibleRuntimeClassMsg = "The asset bundle '%s' could not be loaded because it contains run-time classes of incompatible version. Rebuild the AssetBundle to fix this error.";
+static char const* kIncompatibleRuntimeMsg = "The asset bundle '%s' could not be loaded because it is not compatible with this newer version of the Unity runtime. Rebuild the AssetBundle to fix this error.";
+
+static bool TestScriptCompatibility (std::vector<AssetBundleScriptInfo> const& scripts)
+{
+ bool result = true;
+
+ #if ENABLE_SCRIPTING
+ MonoScriptManager& sm = GetMonoScriptManager();
+
+#if 0 // debug
+ {
+ MonoScriptManager::AllScripts existing = sm.GetAllRuntimeScripts ();
+ printf_console ("LOADED SCRIPTS: %d\n", existing.size ());
+ for (MonoScriptManager::AllScripts::iterator it = existing.begin (), end = existing.end (); it != end; ++it)
+ {
+ printf_console (" %s (%s) hash: %08x\n", (*it)->GetName(), (*it)->GetClassName().c_str(), (*it)->GetPropertiesHash());
+ }
+ }
+
+ {
+ printf_console ("TESTING SCRIPTS: %d\n", scripts.size ());
+ for (size_t i=0, size = scripts.size (); i<size; ++i)
+ printf_console (" [%s.]%s hash: %08x\n", scripts[i].nameSpace.c_str(), scripts[i].className.c_str(), scripts[i].hash);
+ }
+#endif
+
+ for (std::vector<AssetBundleScriptInfo>::const_iterator it = scripts.begin (), end = scripts.end (); it != end; ++it)
+ {
+ if (MonoScript* script = sm.FindRuntimeScript (it->className, it->nameSpace, it->assemblyName))
+ {
+ UInt32 supported = script->GetPropertiesHash ();
+ UInt32 loading = it->hash;
+
+ if (supported != loading)
+ {
+ WarningStringMsg ("AssetBundle loading failed because the %s script serialization hash does not match. Supported: %08x, loading: %08x\n", script->GetScriptFullClassName ().c_str(), (unsigned int)supported, (unsigned int)loading);
+ result = false;
+ }
+ }
+ }
+ #endif
+
+ return result;
+}
+
+static bool TestRuntimeClassCompatibility (std::vector<std::pair<int, UInt32> > const& classes)
+{
+ bool result = true;
+ for (size_t i=0, size = classes.size (); i<size; ++i)
+ {
+ int classID = classes[i].first;
+ UInt32 loading = classes[i].second;
+
+ UInt32 supported = GetBuildSettings ().GetHashOfClass (classID);
+ if (supported != 0 && loading != supported)
+ {
+ WarningStringMsg ("AssetBundle loading failed because the %s class serialization hash does not match. Supported: %08x, loading: %08x\n", Object::ClassIDToString(classID).c_str (), (unsigned int)supported, (unsigned int)loading);
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+bool TestAssetBundleCompatibility (AssetBundle& bundle, const std::string& bundleName, std::string& error)
+{
+ error = string();
+
+ // We only report a single type of incompatibility here even when there are multiple
+ // incompatibilities present in the bundle. However, since the solution to each
+ // incompatibility is rebuilding the asset bundle which will also get rid of
+ // whatever other incompatibilities are present in the bundle, we don't need to
+ // worry about that.
+
+ // Test script class compatibility.
+ if (!TestScriptCompatibility (bundle.m_ScriptCompatibility))
+ {
+ error = Format (kIncompatibleScriptsMsg, bundleName.c_str ());
+ return false;
+ }
+
+ // Test runtime class compatibility.
+ if (!TestRuntimeClassCompatibility (bundle.m_ClassCompatibility))
+ {
+ error = Format (kIncompatibleRuntimeClassMsg, bundleName.c_str ());
+ return false;
+ }
+
+#if !UNITY_WEBPLAYER // We do not allow breaking backwards-compatibility in the webplayer at this point.
+
+ bool isEditorTargetingWebPlayer = false;
+ #if UNITY_EDITOR
+ isEditorTargetingWebPlayer =
+ GetEditorUserBuildSettingsPtr ()
+ && IsWebPlayerTargetPlatform (GetEditorUserBuildSettings ().GetActiveBuildTarget ());
+ #endif
+
+ // Check against our runtime compatibility version to see if there's
+ // been some more profound changes to the runtime that prevent old
+ // asset bundles from working.
+ //
+ // In the case of being in the editor and targeting the webplayer, we
+ // suppress the check given that the webplayer will load the bundle
+ // correctly.
+ if (!isEditorTargetingWebPlayer && bundle.m_RuntimeCompatibility < AssetBundle::CURRENT_RUNTIME_COMPATIBILITY_VERSION)
+ {
+ error = Format (kIncompatibleRuntimeMsg, bundleName.c_str ());
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+static AssetBundle* FindAssetBundleObject (std::string const& assetBundlePath)
+{
+ PersistentManager& pm = GetPersistentManager();
+
+ // An AssetBundle can be the first (AssetBundle) or the second object (StreamedLevel assets file)
+ const int fileID1 = 1, fileID2 = 2;
+
+ int fileID = 0;
+ if (pm.GetClassIDFromPathAndFileID(assetBundlePath, fileID1) == ClassID(AssetBundle))
+ fileID = fileID1;
+ else if (pm.GetClassIDFromPathAndFileID(assetBundlePath, fileID2) == ClassID(AssetBundle))
+ fileID = fileID2;
+ else
+ {
+ // Old streamed scene asset bundle that has no AssetBundle object.
+ return NULL;
+ }
+
+ int instanceID = pm.GetInstanceIDFromPathAndFileID (assetBundlePath, fileID);
+ return dynamic_instanceID_cast<AssetBundle*> (instanceID);
+}
+
+static AssetBundle* InitializeAssetBundle (const std::string& assetBundleName,
+ const std::string& assetBundlePath,
+ AssetBundle::UncompressedFileInfoContainer* uncompressedFiles,
+ UnityWebStream* webStream,
+ bool performCompatibilityChecks = true)
+{
+ PersistentManager& pm = GetPersistentManager();
+ UNUSED(pm);
+
+ // Locate AssetBundle object.
+ AssetBundle* assetBundle = FindAssetBundleObject (assetBundlePath);
+ if (!assetBundle)
+ {
+ // We used to not include AssetBundle objects in streamed scene
+ // asset bundles that had type trees. This is no longer the case
+ // but to handle this case, we create an AssetBundle object on
+ // demand (like before).
+
+ assetBundle = NEW_OBJECT (AssetBundle);
+ assetBundle->Reset ();
+ assetBundle->AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ // Set to incompatible runtime version. Will only load on
+ // webplayer.
+ assetBundle->m_RuntimeCompatibility = 0;
+ }
+
+ if (assetBundle->m_UncompressedFileInfo)
+ UNITY_DELETE (assetBundle->m_UncompressedFileInfo, kMemFile);
+
+ // Associate uncompressed files with asset bundle, if we have them
+ // (will only be the case for local asset bundles).
+ if (uncompressedFiles)
+ {
+ UNITY_TRANSFER_OWNERSHIP (uncompressedFiles, kMemFile, assetBundle);
+ assetBundle->m_UncompressedFileInfo = uncompressedFiles;
+ }
+
+ // Associate webstream with asset bundle, if we have one
+ // (will only be the case for downloaded asset bundles).
+#if ENABLE_WWW
+ if (webStream)
+ {
+ assetBundle->m_UnityWebStream = webStream;
+ assetBundle->m_UnityWebStream->Retain ();
+ webStream->SetCachedAssetBundle (assetBundle->GetInstanceID());
+ }
+#endif
+
+ // Make sure the asset bundle can be loaded into this
+ // build of the player.
+ if (performCompatibilityChecks)
+ {
+ string errorMessage;
+ bool isCompatible = TestAssetBundleCompatibility (*assetBundle, assetBundleName, errorMessage);
+ if (!isCompatible)
+ {
+ ErrorString (errorMessage);
+ UnloadAssetBundle (*assetBundle, true);
+ return NULL;
+ }
+ }
+
+ return assetBundle;
+}
+
+#if ENABLE_WWW
+static bool LooksLikeStreamedSceneBundle (FileStream* data)
+{
+ if (data->m_Files.size () == 0)
+ return false;
+
+ // If first or second file name starts with "BuildPlayer-", it's
+ // probably a streamed scene bundle. In case it has extra resources,
+ // that will be the first file.
+ const bool firstOrSecondFileIsSceneData =
+ data->m_Files[0].name.find ("BuildPlayer-") == 0 ||
+ (data->m_Files.size () > 1 && data->m_Files[1].name.find ("BuildPlayer-") == 0);
+
+ return firstOrSecondFileIsSceneData;
+}
+
+static bool LooksLikeCustomAssetBundle (FileStream* data)
+{
+ if (data->m_Files.size () == 0)
+ return false;
+
+ // If the first file name starts with "CustomAssetBundle" or "CAB",
+ // it's probably a custom asset bundle.
+ const bool isCustomAssetBundle =
+ data->m_Files[0].name.find ("CustomAssetBundle") == 0 ||
+ data->m_Files[0].name.find ("CAB" ) == 0;
+
+ return isCustomAssetBundle;
+}
+
+static bool LooksLikeValidAssetBundle (FileStream* data)
+{
+ return LooksLikeStreamedSceneBundle (data)
+ || LooksLikeCustomAssetBundle( data );
+}
+
+static AssetBundle* ExtractAssetBundle(UnityWebStream* unityweb, const char* url, bool performCompatibilityChecks = true)
+{
+ if (!unityweb || !unityweb->GetFileStream())
+ return NULL;
+
+ FileStream* data = (FileStream*)unityweb->GetFileStream();
+
+ // Return last asset bundle instance if already accessed.
+ if (data->m_Files.size() >= 1 && dynamic_instanceID_cast<AssetBundle*> (unityweb->GetCachedAssetBundle()))
+ return dynamic_instanceID_cast<AssetBundle*> (unityweb->GetCachedAssetBundle());
+
+ ///@TODO: the UnityWebStream should contain information on if the file is an asset bundle instead
+ if (!LooksLikeValidAssetBundle (data))
+ {
+ ErrorString("The unity3d file is not a valid asset bundle.");
+ return NULL;
+ }
+
+ PersistentManager& pm = GetPersistentManager();
+ pm.Lock();
+
+ for (FileStream::iterator i=data->begin();i != data->end();i++)
+ {
+ if (pm.IsStreamLoaded(i->name))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because another asset bundle with the same files are already loaded", url));
+ return NULL;
+ }
+ }
+
+ // Load all memory streams
+ for (FileStream::iterator i=data->begin();i != data->end();i++)
+ {
+ if (!pm.LoadMemoryBlockStream(i->name, i->blocks, i->offset, i->end, url))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because it was not built with the right version or build target.", url));
+ return NULL;
+ }
+ }
+
+ pm.Unlock();
+
+ ////WORKAROUND!
+ //// There was a bug in 4.2 where script compatibility information was *always* written
+ //// out into streamed scene asset bundles which in turn makes us always check these
+ //// hashes on loading and reject bundles if they don't match. As we *always* build
+ //// streamed scene bundles with type trees when targeting the webplayer, this makes us
+ //// not load bundles we can actually load. Work around this issue here by disabling
+ //// compatibility checks altogether when loading a streamed scene bundle into the webplayer.
+ #if UNITY_WEBPLAYER
+ if (LooksLikeStreamedSceneBundle (data))
+ performCompatibilityChecks = false;
+ #endif
+
+ const string &path = data->begin ()->name;
+ return InitializeAssetBundle (url, path, NULL, unityweb, performCompatibilityChecks);
+}
+
+AssetBundle *ExtractAssetBundle(WWW &www)
+{
+ if (!www.HasDownloadedOrMayBlock ())
+ return NULL;
+ www.BlockUntilDone();
+
+ #if ENABLE_CACHING
+ if (www.GetType() == kWWWTypeCached)
+ return static_cast<WWWCached&>(www).GetAssetBundle();
+ #endif
+
+ return ExtractAssetBundle(www.GetUnityWebStream(), www.GetUrl());
+}
+
+AssetBundleCreateRequest::AssetBundleCreateRequest( const UInt8* dataPtr, int dataSize )
+ : m_UnityWebStream(0)
+ , m_AssetBundle(0)
+ , m_EnableCompatibilityChecks (true)
+{
+ UnityWebStreamHeader header;
+
+ int result = ParseStreamHeader (header, dataPtr, dataPtr + dataSize);
+ if (result)
+ {
+ if (result == 1)
+ {
+ ErrorString("Asset bundle is incomplete");
+ }
+ else if(result == 2)
+ {
+ ErrorString("Error parsing asset bundle");
+ }
+
+ m_Progress = 1.f;
+ UnityMemoryBarrier();
+ m_Complete = true;
+ return;
+ }
+
+ m_UnityWebStream = UNITY_NEW_AS_ROOT(UnityWebStream(NULL, 0, 0), kMemFile, "WebStream", "");
+#if SUPPORT_THREADS
+ m_UnityWebStream->SetDecompressionPriority(GetPreloadManager().GetThreadPriority());
+#endif
+ m_UnityWebStream->Retain();
+
+ m_UnityWebStream->FeedDownloadData(dataPtr, dataSize, true);
+
+ GetPreloadManager().AddToQueue(this);
+}
+
+AssetBundleCreateRequest::~AssetBundleCreateRequest()
+{
+ if (m_UnityWebStream != NULL)
+ m_UnityWebStream->Release();
+}
+
+void AssetBundleCreateRequest::Perform ()
+{
+ if (m_UnityWebStream == NULL)
+ return;
+ m_UnityWebStream->WaitForThreadDecompression();
+}
+void AssetBundleCreateRequest::IntegrateMainThread ()
+{
+ m_AssetBundle = ExtractAssetBundle(m_UnityWebStream, "<unknown>", m_EnableCompatibilityChecks);
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+float AssetBundleCreateRequest::GetProgress ()
+{
+ if (m_UnityWebStream == NULL)
+ return 1.f;
+ m_UnityWebStream->UpdateProgress();
+ m_Progress = m_UnityWebStream->GetProgressUntilLoadable();
+ return m_Progress;
+}
+
+#endif
+
+static void ForcePreload (AssetBundle& bundle, const AssetBundle::AssetInfo& info)
+{
+ for (int i=0;i<info.preloadSize;i++)
+ {
+ PPtr<Object> preload = bundle.m_PreloadTable[i + info.preloadIndex];
+ preload.IsValid();
+ }
+}
+
+///@TODO: For 4.0 we should remove this function and make AssetBundle.Load use the LoadAsync code path and wait for completion.
+
+static void ProcessAssetBundleEntries(AssetBundle& bundle, AssetBundle::range entries, ScriptingObjectPtr systemTypeInstance, vector<Object*>& output, bool stopAfterOne)
+{
+#if ENABLE_SCRIPTING
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+
+ for (AssetBundle::iterator i=entries.first;i != entries.second;i++)
+ {
+ Object* obj = i->second.asset;
+ if (obj == NULL)
+ continue;
+
+ ScriptingObjectPtr o = Scripting::ScriptingWrapperFor(obj);
+ if (o && scripting_class_is_subclass_of(scripting_object_get_class(o, GetScriptingTypeRegistry()), klass))
+ {
+ ForcePreload(bundle, i->second);
+ output.push_back(obj);
+ if (stopAfterOne) return;
+ }
+
+ Unity::GameObject* go = dynamic_pptr_cast<GameObject*> (obj);
+ if (go != NULL)
+ {
+ o = ScriptingGetComponentOfType(*go, systemTypeInstance, false);
+ if (o != SCRIPTING_NULL)
+ {
+ ForcePreload(bundle, i->second);
+ output.push_back(ScriptingObjectToObject<Object>(o));
+ if (stopAfterOne) return;
+ }
+ }
+ }
+#endif
+}
+
+Object* LoadNamedObjectFromAssetBundle (AssetBundle& bundle, const std::string& name, ScriptingObjectPtr type)
+{
+
+ string lowerName = ToLower(name);
+ AssetBundle::range found = bundle.GetPathRange(lowerName);
+
+ vector<Object*> result;
+ ProcessAssetBundleEntries(bundle,found,type,result,true);
+ if (result.empty())
+ return NULL;
+
+ return result[0];
+}
+
+Object* LoadMainObjectFromAssetBundle (AssetBundle& bundle)
+{
+ PreloadLevelOperation::PreloadBundleSync (bundle, "");
+ return bundle.m_MainAsset.asset;
+}
+
+void LoadAllFromAssetBundle (AssetBundle& assetBundle, ScriptingObjectPtr type, vector<Object* >& output)
+{
+ AssetBundle::range found = assetBundle.GetAll();
+ ProcessAssetBundleEntries(assetBundle,found,type,output,false);
+}
+
+namespace UnityConsoleStream_Static
+{
+
+ static bool ReadBigEndian (const UInt8*& cur, const UInt8* dataEnd, UInt32& data)
+ {
+ if (cur + sizeof(SInt32) > dataEnd)
+ return false;
+ data = *reinterpret_cast<const SInt32*> (cur);
+#if UNITY_LITTLE_ENDIAN
+ SwapEndianBytes(data);
+#endif
+ cur += sizeof(SInt32);
+ return true;
+ }
+
+ static bool ReadString (const UInt8*& cur, const UInt8* dataEnd, std::string& data)
+ {
+ int length = 0;
+ while (true)
+ {
+ if (cur + length >= dataEnd)
+ return false;
+
+ if (cur[length] == '\0')
+ break;
+
+ length++;
+ }
+
+ data.assign (cur, cur + length);
+ cur += length + 1;
+ return true;
+ }
+
+} // namespace UnityWebStream_Static
+
+
+bool ParseUncompressedFileHeader(AssetBundle::UncompressedFileInfoContainer* files, const UInt8* cur, const UInt8* headerEnd, int offset)
+{
+ using namespace UnityConsoleStream_Static;
+ UInt32 fileCount;
+ if (!ReadBigEndian(cur, headerEnd, fileCount))
+ return false;
+
+ files->reserve (fileCount);
+
+ for (int i = 0; i < fileCount; i++)
+ {
+ AssetBundle::UncompressedFileInfo fileInfo;
+
+ if (!ReadString(cur, headerEnd, fileInfo.fileName)) return false;
+ if (!ReadBigEndian(cur, headerEnd, fileInfo.offset)) return false;
+ else
+ {
+ fileInfo.offset += offset;
+ }
+ if (!ReadBigEndian(cur, headerEnd, fileInfo.size)) return false;
+
+ files->push_back(fileInfo);
+ }
+
+ return true;
+}
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName)
+{
+ using namespace UnityConsoleStream_Static;
+ File file;
+ if (!file.Open (assetBundlePathName, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ return NULL;
+
+#define ExitAndCloseFile() {file.Close(); return NULL;}
+
+ const int kBundleHeaderSize = sizeof("UnityRaw") +
+ sizeof(SInt32) +
+ sizeof(UNITY_WEB_BUNDLE_VERSION) +
+ sizeof(UNITY_WEB_MINIMUM_REVISION) +
+ sizeof(SInt32) * 4;
+
+ UInt8 prefix[kBundleHeaderSize];
+ int bytesRead = file.Read(0, prefix, kBundleHeaderSize);
+ if (bytesRead != kBundleHeaderSize)
+ {
+ ErrorString("Error while reading asset bundle header!");
+ ExitAndCloseFile();
+ }
+
+ // read prefix
+ std::string streamId, unityVersion, minimumRevision;
+ UInt32 streamVersion;
+
+ UInt8 const* it = prefix, *end = prefix + kBundleHeaderSize;
+ if (!ReadString(it, end, streamId))
+ ExitAndCloseFile()
+ if (streamId != "UnityRaw")
+ {
+ ErrorStringMsg("This asset bundle was not created with UncompressedAssetBundle flag, expected id 'UnityRaw', got '%s'", streamId.c_str());
+ ExitAndCloseFile();
+ }
+ if (!ReadBigEndian(it, end, streamVersion))
+ ExitAndCloseFile();
+ if (!ReadString(it, end, unityVersion))
+ ExitAndCloseFile();
+ if (!ReadString(it, end, minimumRevision))
+ ExitAndCloseFile();
+
+ UInt32 byteStart, headerSize, numberOfLevelsToDownloadBeforeStreaming, levelCount, completeFileSize, fileInfoHeaderSize;
+
+ if (!ReadBigEndian(it, end, byteStart))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, headerSize))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, numberOfLevelsToDownloadBeforeStreaming))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, levelCount))
+ ExitAndCloseFile();
+
+ UInt8* fullHeader = (UInt8*)alloca (headerSize);
+ file.Read(0, fullHeader, headerSize);
+
+ it = fullHeader + kBundleHeaderSize;
+ end = fullHeader + headerSize;
+
+ UInt32 dummyCompressed, dummyUncompressed;
+ for (UInt32 i = 0; i < levelCount; i++)
+ {
+ if (!ReadBigEndian(it, end, dummyCompressed))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, dummyUncompressed))
+ ExitAndCloseFile();
+ }
+ if (!ReadBigEndian(it, end, completeFileSize))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, fileInfoHeaderSize))
+ ExitAndCloseFile();
+
+ void* fileContainerRoot = UNITY_NEW_AS_ROOT(int,kMemFile,"Temp","");
+ AssetBundle::UncompressedFileInfoContainer* files;
+ {
+ SET_ALLOC_OWNER(fileContainerRoot);
+ files = UNITY_NEW(AssetBundle::UncompressedFileInfoContainer, kMemFile);
+ }
+ UInt8* fileInfoHeader = (UInt8*)alloca (fileInfoHeaderSize);
+ file.Read (headerSize, fileInfoHeader, fileInfoHeaderSize);
+ file.Close();
+
+#undef ExitAndCloseFile
+
+ if (ParseUncompressedFileHeader(files, fileInfoHeader, fileInfoHeader + fileInfoHeaderSize, headerSize) == false)
+ {
+ ErrorString("Failed to parsed asset bundle header");
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+
+ if (files->size() == 0 || (files->begin()->fileName.find("BuildPlayer-") != 0 && files->begin()->fileName.find("CustomAssetBundle") != 0 && files->begin()->fileName.find("CAB") != 0))
+ {
+ ErrorString("The unity3d file is not a valid asset bundle.");
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+
+ PersistentManager& pm = GetPersistentManager();
+ pm.Lock();
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator i = files->begin(); i != files->end(); ++i)
+ {
+ if (pm.IsStreamLoaded(i->fileName))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because another asset bundle with the same files are already loaded.", assetBundlePathName.c_str()));
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+ }
+
+ // Load all memory streams
+ for (AssetBundle::UncompressedFileInfoContainer::iterator i=files->begin(); i != files->end(); ++i)
+ {
+ // TODO: check flags
+ if (!pm.LoadExternalStream(i->fileName, assetBundlePathName, kSerializeGameRelease, i->offset))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because it was not built with the right version or build target.", assetBundlePathName.c_str()));
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+ }
+
+ pm.Unlock();
+
+ const string &path = files->begin ()->fileName;
+ return InitializeAssetBundle (assetBundlePathName, path, files, NULL);
+}
+#else
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName)
+{
+ ErrorString("Failed to load asset bundle (not supported).");
+ return NULL;
+}
+#endif