예제 #1
0
        /// <summary>
        /// Performs first part of the loading sequence, by deserializing the package but without processing anything yet.
        /// </summary>
        /// <param name="log">The log.</param>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">
        /// log
        /// or
        /// filePath
        /// </exception>
        internal static Package LoadRaw(ILogger log, string filePath)
        {
            if (log == null)
            {
                throw new ArgumentNullException("log");
            }
            if (filePath == null)
            {
                throw new ArgumentNullException("filePath");
            }

            filePath = FileUtility.GetAbsolutePath(filePath);

            if (!File.Exists(filePath))
            {
                log.Error("Package file [{0}] was not found", filePath);
                return(null);
            }

            try
            {
                var package = AssetSerializer.Load <Package>(filePath, log);
                package.FullPath = filePath;
                package.IsDirty  = false;

                return(package);
            }
            catch (Exception ex)
            {
                log.Error("Error while pre-loading package [{0}]", ex, filePath);
            }

            return(null);
        }
예제 #2
0
        /// <summary>
        /// Loads the templates.
        /// </summary>
        /// <param name="log">The log result.</param>
        private void LoadTemplates(ILogger log)
        {
            foreach (var templateDir in TemplateFolders)
            {
                foreach (var filePath in templateDir.Files)
                {
                    try
                    {
                        var file = new FileInfo(filePath);
                        if (!file.Exists)
                        {
                            log.Warning("Template [{0}] does not exist ", file);
                            continue;
                        }

                        var templateDescription = AssetSerializer.Load <TemplateDescription>(file.FullName);
                        templateDescription.FullPath = file.FullName;
                        Templates.Add(templateDescription);
                    }
                    catch (Exception ex)
                    {
                        log.Error("Error while loading template from [{0}]", ex, filePath);
                    }
                }
            }
        }
예제 #3
0
 /// <summary>
 /// Gets the package identifier from file.
 /// </summary>
 /// <param name="filePath">The file path.</param>
 /// <returns>Guid.</returns>
 /// <exception cref="System.ArgumentNullException">
 /// log
 /// or
 /// filePath
 /// </exception>
 public static Guid GetPackageIdFromFile(string filePath)
 {
     if (filePath == null)
     {
         throw new ArgumentNullException("filePath");
     }
     return(AssetSerializer.Load <Package>(filePath).Id);
 }
예제 #4
0
        private static Asset LoadAsset(ILogger log, string assetFullPath, string assetPath, UFile fileUPath)
        {
            AssetMigration.MigrateAssetIfNeeded(log, assetFullPath);

            var asset = AssetSerializer.Load <Asset>(assetFullPath);

            // Set location on source code asset
            var sourceCodeAsset = asset as SourceCodeAsset;

            if (sourceCodeAsset != null)
            {
                // Use an id generated from the location instead of the default id
                sourceCodeAsset.Id = SourceCodeAsset.GenerateGuidFromLocation(assetPath);
                sourceCodeAsset.AbsoluteSourceLocation = fileUPath;
            }

            return(asset);
        }
예제 #5
0
        private static Asset LoadAsset(ILogger log, string assetFullPath, string assetPath, UFile fileUPath, byte[] assetContent)
        {
            var asset = assetContent != null
                ? (Asset)AssetSerializer.Load(new MemoryStream(assetContent), Path.GetExtension(assetFullPath), log)
                : AssetSerializer.Load <Asset>(assetFullPath, log);

            // Set location on source code asset
            var sourceCodeAsset = asset as SourceCodeAsset;

            if (sourceCodeAsset != null)
            {
                // Use an id generated from the location instead of the default id
                sourceCodeAsset.Id = SourceCodeAsset.GenerateGuidFromLocation(assetPath);
                sourceCodeAsset.AbsoluteSourceLocation = fileUPath;
            }

            return(asset);
        }
예제 #6
0
        /// <summary>
        /// Performs first part of the loading sequence, by deserializing the package but without processing anything yet.
        /// </summary>
        /// <param name="log">The log.</param>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">
        /// log
        /// or
        /// filePath
        /// </exception>
        internal static Package LoadRaw(ILogger log, string filePath)
        {
            if (log == null)
            {
                throw new ArgumentNullException("log");
            }
            if (filePath == null)
            {
                throw new ArgumentNullException("filePath");
            }

            filePath = FileUtility.GetAbsolutePath(filePath);

            if (!File.Exists(filePath))
            {
                log.Error("Package file [{0}] was not found", filePath);
                return(null);
            }

            try
            {
                bool aliasOccurred;
                var  packageFile = new PackageLoadingAssetFile(filePath, Path.GetDirectoryName(filePath));
                AssetMigration.MigrateAssetIfNeeded(log, packageFile);

                var package = packageFile.AssetContent != null
                    ? (Package)AssetSerializer.Load(new MemoryStream(packageFile.AssetContent), Path.GetExtension(filePath), log, out aliasOccurred)
                    : AssetSerializer.Load <Package>(filePath, log, out aliasOccurred);

                package.FullPath = filePath;
                package.IsDirty  = packageFile.AssetContent != null || aliasOccurred;

                return(package);
            }
            catch (Exception ex)
            {
                log.Error("Error while pre-loading package [{0}]", ex, filePath);
            }

            return(null);
        }
예제 #7
0
        /// <summary>
        /// Loads only the package description but not assets or plugins.
        /// </summary>
        /// <param name="log">The log to receive error messages.</param>
        /// <param name="filePath">The file path.</param>
        /// <param name="loadParametersArg">The load parameters argument.</param>
        /// <returns>A package.</returns>
        /// <exception cref="System.ArgumentNullException">log
        /// or
        /// filePath</exception>
        public static Package Load(ILogger log, string filePath, PackageLoadParameters loadParametersArg = null)
        {
            if (log == null)
            {
                throw new ArgumentNullException("log");
            }
            if (filePath == null)
            {
                throw new ArgumentNullException("filePath");
            }

            filePath = FileUtility.GetAbsolutePath(filePath);

            if (!File.Exists(filePath))
            {
                log.Error("Package file [{0}] was not found", filePath);
                return(null);
            }

            var loadParameters = loadParametersArg ?? PackageLoadParameters.Default();

            try
            {
                var package = AssetSerializer.Load <Package>(filePath);
                package.FullPath = filePath;
                package.IsDirty  = false;

                // Load assembly references
                if (loadParameters.LoadAssemblyReferences)
                {
                    package.LoadAssemblyReferencesForPackage(log, loadParameters);
                }

                // Load assets
                if (loadParameters.AutoLoadTemporaryAssets)
                {
                    package.LoadTemporaryAssets(log, loadParameters.CancelToken);
                }

                // Convert UPath to absolute
                if (loadParameters.ConvertUPathToAbsolute)
                {
                    var analysis = new PackageAnalysis(package, new PackageAnalysisParameters()
                    {
                        ConvertUPathTo     = UPathType.Absolute,
                        IsProcessingUPaths = true,                        // This is done already by Package.Load
                        SetDirtyFlagOnAssetWhenFixingAbsoluteUFile = true // When loading tag attributes that have an absolute file
                    });
                    analysis.Run(log);
                }

                // Load templates
                package.LoadTemplates(log);

                return(package);
            }
            catch (Exception ex)
            {
                log.Error("Error while pre-loading package [{0}]", ex, filePath);
            }

            return(null);
        }
예제 #8
0
        /// <summary>
        /// Saves this package and all dirty assets. See remarks.
        /// </summary>
        /// <param name="log">The log.</param>
        /// <exception cref="System.ArgumentNullException">log</exception>
        /// <remarks>When calling this method directly, it does not handle moving assets between packages.
        /// Call <see cref="PackageSession.Save" /> instead.</remarks>
        public void Save(ILogger log)
        {
            if (log == null)
            {
                throw new ArgumentNullException("log");
            }

            if (FullPath == null)
            {
                log.Error(this, null, AssetMessageCode.PackageCannotSave, "null");
                return;
            }

            // Use relative paths when saving
            var analysis = new PackageAnalysis(this, new PackageAnalysisParameters()
            {
                SetDirtyFlagOnAssetWhenFixingUFile = false,
                ConvertUPathTo     = UPathType.Relative,
                IsProcessingUPaths = true
            });

            analysis.Run(log);

            try
            {
                // Update source folders
                UpdateSourceFolders();

                if (IsDirty)
                {
                    try
                    {
                        // Notifies the dependency manager that a package with the specified path is being saved
                        if (session != null && session.HasDependencyManager)
                        {
                            session.DependencyManager.AddFileBeingSaveDuringSessionSave(FullPath);
                        }

                        AssetSerializer.Save(FullPath, this);

                        IsDirty = false;
                    }
                    catch (Exception ex)
                    {
                        log.Error(this, null, AssetMessageCode.PackageCannotSave, ex, FullPath);
                        return;
                    }
                }

                foreach (var asset in Assets)
                {
                    if (asset.IsDirty)
                    {
                        var assetPath = asset.FullPath;
                        try
                        {
                            // Notifies the dependency manager that an asset with the specified path is being saved
                            if (session != null && session.HasDependencyManager)
                            {
                                session.DependencyManager.AddFileBeingSaveDuringSessionSave(assetPath);
                            }

                            // Incject a copy of the base into the current asset when saving
                            var assetBase = asset.Asset.Base;
                            if (assetBase != null && !assetBase.IsRootImport)
                            {
                                var assetBaseItem = session != null?session.FindAsset(assetBase.Id) : Assets.Find(assetBase.Id);

                                if (assetBaseItem != null)
                                {
                                    var newBase = (Asset)AssetCloner.Clone(assetBaseItem.Asset);
                                    newBase.Base     = null;
                                    asset.Asset.Base = new AssetBase(asset.Asset.Base.Location, newBase);
                                }
                            }

                            AssetSerializer.Save(assetPath, asset.Asset);
                            asset.IsDirty = false;
                        }
                        catch (Exception ex)
                        {
                            log.Error(this, asset.ToReference(), AssetMessageCode.AssetCannotSave, ex, assetPath);
                        }
                    }
                }

                Assets.IsDirty = false;

                // Save properties like the Paradox version used
                PackageSessionHelper.SaveProperties(this);
            }
            finally
            {
                // Rollback all relative UFile to absolute paths
                analysis.Parameters.ConvertUPathTo = UPathType.Absolute;
                analysis.Run();
            }
        }
예제 #9
0
        public static bool MigrateAssetIfNeeded(AssetMigrationContext context, PackageLoadingAssetFile loadAsset, string dependencyName, PackageVersion untilVersion = null)
        {
            var assetFullPath = loadAsset.FilePath.FullPath;

            // Determine if asset was Yaml or not
            var assetFileExtension = Path.GetExtension(assetFullPath);

            if (assetFileExtension == null)
            {
                return(false);
            }

            assetFileExtension = assetFileExtension.ToLowerInvariant();

            var serializer = AssetSerializer.FindSerializer(assetFileExtension);

            if (!(serializer is AssetYamlSerializer))
            {
                return(false);
            }

            // We've got a Yaml asset, let's get expected and serialized versions
            var            serializedVersion = PackageVersion.Zero;
            PackageVersion expectedVersion;
            Type           assetType;

            // Read from Yaml file the asset version and its type (to get expected version)
            // Note: It tries to read as few as possible (SerializedVersion is expected to be right after Id, so it shouldn't try to read further than that)
            using (var assetStream = loadAsset.OpenStream())
                using (var streamReader = new StreamReader(assetStream))
                {
                    var yamlEventReader = new EventReader(new Parser(streamReader));

                    // Skip header
                    yamlEventReader.Expect <StreamStart>();
                    yamlEventReader.Expect <DocumentStart>();
                    var mappingStart = yamlEventReader.Expect <MappingStart>();

                    var  yamlSerializerSettings = YamlSerializer.GetSerializerSettings();
                    var  tagTypeRegistry        = yamlSerializerSettings.TagTypeRegistry;
                    bool typeAliased;
                    assetType = tagTypeRegistry.TypeFromTag(mappingStart.Tag, out typeAliased);

                    var expectedVersions = AssetRegistry.GetCurrentFormatVersions(assetType);
                    expectedVersion = expectedVersions?.FirstOrDefault(x => x.Key == dependencyName).Value ?? PackageVersion.Zero;

                    Scalar assetKey;
                    while ((assetKey = yamlEventReader.Allow <Scalar>()) != null)
                    {
                        // Only allow Id before SerializedVersion
                        if (assetKey.Value == nameof(Asset.Id))
                        {
                            yamlEventReader.Skip();
                        }
                        else if (assetKey.Value == nameof(Asset.SerializedVersion))
                        {
                            // Check for old format: only a scalar
                            var scalarVersion = yamlEventReader.Allow <Scalar>();
                            if (scalarVersion != null)
                            {
                                serializedVersion = PackageVersion.Parse("0.0." + Convert.ToInt32(scalarVersion.Value, CultureInfo.InvariantCulture));

                                // Let's update to new format
                                using (var yamlAsset = loadAsset.AsYamlAsset())
                                {
                                    yamlAsset.DynamicRootNode.RemoveChild(nameof(Asset.SerializedVersion));
                                    AssetUpgraderBase.SetSerializableVersion(yamlAsset.DynamicRootNode, dependencyName, serializedVersion);

                                    var baseBranch = yamlAsset.DynamicRootNode[Asset.BaseProperty];
                                    if (baseBranch != null)
                                    {
                                        var baseAsset = baseBranch["Asset"];
                                        if (baseAsset != null)
                                        {
                                            baseAsset.RemoveChild(nameof(Asset.SerializedVersion));
                                            AssetUpgraderBase.SetSerializableVersion(baseAsset, dependencyName, serializedVersion);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // New format: package => version mapping
                                yamlEventReader.Expect <MappingStart>();

                                while (!yamlEventReader.Accept <MappingEnd>())
                                {
                                    var packageName    = yamlEventReader.Expect <Scalar>().Value;
                                    var packageVersion = PackageVersion.Parse(yamlEventReader.Expect <Scalar>().Value);

                                    // For now, we handle only one dependency at a time
                                    if (packageName == dependencyName)
                                    {
                                        serializedVersion = packageVersion;
                                    }
                                }

                                yamlEventReader.Expect <MappingEnd>();
                            }
                            break;
                        }
                        else
                        {
                            // If anything else than Id or SerializedVersion, let's stop
                            break;
                        }
                    }
                }

            if (serializedVersion > expectedVersion)
            {
                // Try to open an asset newer than what we support (probably generated by a newer Xenko)
                throw new InvalidOperationException($"Asset of type {assetType} has been serialized with newer version {serializedVersion}, but only version {expectedVersion} is supported. Was this asset created with a newer version of Xenko?");
            }

            if (serializedVersion < expectedVersion)
            {
                // Perform asset upgrade
                context.Log.Verbose("{0} needs update, from version {1} to version {2}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);

                using (var yamlAsset = loadAsset.AsYamlAsset())
                {
                    var yamlRootNode = yamlAsset.RootNode;

                    // Check if there is any asset updater
                    var assetUpgraders = AssetRegistry.GetAssetUpgraders(assetType, dependencyName);
                    if (assetUpgraders == null)
                    {
                        throw new InvalidOperationException($"Asset of type {assetType} should be updated from version {serializedVersion} to {expectedVersion}, but no asset migration path was found");
                    }

                    // Instantiate asset updaters
                    var currentVersion = serializedVersion;
                    while (currentVersion != expectedVersion)
                    {
                        PackageVersion targetVersion;
                        // This will throw an exception if no upgrader is available for the given version, exiting the loop in case of error.
                        var upgrader = assetUpgraders.GetUpgrader(currentVersion, out targetVersion);

                        // Stop if the next version would be higher than what is expected
                        if (untilVersion != null && targetVersion > untilVersion)
                        {
                            break;
                        }

                        upgrader.Upgrade(context, dependencyName, currentVersion, targetVersion, yamlRootNode, loadAsset);
                        currentVersion = targetVersion;
                    }

                    // Make sure asset is updated to latest version
                    YamlNode       serializedVersionNode;
                    PackageVersion newSerializedVersion = null;
                    if (yamlRootNode.Children.TryGetValue(new YamlScalarNode(nameof(Asset.SerializedVersion)), out serializedVersionNode))
                    {
                        var newSerializedVersionForDefaultPackage = ((YamlMappingNode)serializedVersionNode).Children[new YamlScalarNode(dependencyName)];
                        newSerializedVersion = PackageVersion.Parse(((YamlScalarNode)newSerializedVersionForDefaultPackage).Value);
                    }

                    if (untilVersion == null && newSerializedVersion != expectedVersion)
                    {
                        throw new InvalidOperationException($"Asset of type {assetType} was migrated, but still its new version {newSerializedVersion} doesn't match expected version {expectedVersion}.");
                    }

                    context.Log.Info("{0} updated from version {1} to version {2}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);
                }

                return(true);
            }

            return(false);
        }
예제 #10
0
        public static bool MigrateAssetIfNeeded(AssetMigrationContext context, PackageLoadingAssetFile loadAsset)
        {
            var assetFullPath = loadAsset.FilePath.FullPath;

            // Determine if asset was Yaml or not
            var assetFileExtension = Path.GetExtension(assetFullPath);

            if (assetFileExtension == null)
            {
                return(false);
            }

            assetFileExtension = assetFileExtension.ToLowerInvariant();

            var serializer = AssetSerializer.FindSerializer(assetFileExtension);

            if (!(serializer is AssetYamlSerializer))
            {
                return(false);
            }

            // We've got a Yaml asset, let's get expected and serialized versions
            var  serializedVersion = 0;
            int  expectedVersion;
            Type assetType;

            // Read from Yaml file the asset version and its type (to get expected version)
            // Note: It tries to read as few as possible (SerializedVersion is expected to be right after Id, so it shouldn't try to read further than that)
            using (var assetStream = loadAsset.OpenStream())
                using (var streamReader = new StreamReader(assetStream))
                {
                    var yamlEventReader = new EventReader(new Parser(streamReader));

                    // Skip header
                    yamlEventReader.Expect <StreamStart>();
                    yamlEventReader.Expect <DocumentStart>();
                    var mappingStart = yamlEventReader.Expect <MappingStart>();

                    var  yamlSerializerSettings = YamlSerializer.GetSerializerSettings();
                    var  tagTypeRegistry        = yamlSerializerSettings.TagTypeRegistry;
                    bool typeAliased;
                    assetType = tagTypeRegistry.TypeFromTag(mappingStart.Tag, out typeAliased);

                    expectedVersion = AssetRegistry.GetCurrentFormatVersion(assetType);

                    Scalar assetKey;
                    while ((assetKey = yamlEventReader.Allow <Scalar>()) != null)
                    {
                        // Only allow Id before SerializedVersion
                        if (assetKey.Value == "Id")
                        {
                            yamlEventReader.Skip();
                            continue;
                        }
                        if (assetKey.Value == "SerializedVersion")
                        {
                            serializedVersion = Convert.ToInt32(yamlEventReader.Expect <Scalar>().Value, CultureInfo.InvariantCulture);
                            break;
                        }
                    }
                }

            if (serializedVersion > expectedVersion)
            {
                // Try to open an asset newer than what we support (probably generated by a newer Paradox)
                throw new InvalidOperationException(string.Format("Asset of type {0} has been serialized with newer version {1}, but only version {2} is supported. Was this asset created with a newer version of Paradox?", assetType, serializedVersion, expectedVersion));
            }

            if (serializedVersion < expectedVersion)
            {
                // Perform asset upgrade
                context.Log.Verbose("{0} needs update, from version {1} to version {2}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);

                // transform the stream into string.
                string assetAsString;
                using (var assetStream = loadAsset.OpenStream())
                    using (var assetStreamReader = new StreamReader(assetStream, Encoding.UTF8))
                    {
                        assetAsString = assetStreamReader.ReadToEnd();
                    }

                // Load the asset as a YamlNode object
                var input      = new StringReader(assetAsString);
                var yamlStream = new YamlStream();
                yamlStream.Load(input);
                var yamlRootNode = (YamlMappingNode)yamlStream.Documents[0].RootNode;

                // Check if there is any asset updater
                var assetUpgraders = AssetRegistry.GetAssetUpgraders(assetType);
                if (assetUpgraders == null)
                {
                    throw new InvalidOperationException(string.Format("Asset of type {0} should be updated from version {1} to {2}, but no asset migration path was found", assetType, serializedVersion, expectedVersion));
                }

                // Instantiate asset updaters
                var currentVersion = serializedVersion;
                while (currentVersion != expectedVersion)
                {
                    int targetVersion;
                    // This will throw an exception if no upgrader is available for the given version, exiting the loop in case of error.
                    var upgrader = assetUpgraders.GetUpgrader(currentVersion, out targetVersion);
                    upgrader.Upgrade(context, currentVersion, targetVersion, yamlRootNode, loadAsset);
                    currentVersion = targetVersion;
                }

                // Make sure asset is updated to latest version
                YamlNode serializedVersionNode;
                var      newSerializedVersion = 0;
                if (yamlRootNode.Children.TryGetValue(new YamlScalarNode("SerializedVersion"), out serializedVersionNode))
                {
                    newSerializedVersion = Convert.ToInt32(((YamlScalarNode)serializedVersionNode).Value);
                }

                if (newSerializedVersion != expectedVersion)
                {
                    throw new InvalidOperationException(string.Format("Asset of type {0} was migrated, but still its new version {1} doesn't match expected version {2}.", assetType, newSerializedVersion, expectedVersion));
                }

                context.Log.Info("{0} updated from version {1} to version {2}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);

                var preferredIndent = YamlSerializer.GetSerializerSettings().PreferredIndent;

                // Save asset back to disk
                using (var memoryStream = new MemoryStream())
                {
                    using (var streamWriter = new StreamWriter(memoryStream))
                    {
                        yamlStream.Save(streamWriter, true, preferredIndent);
                    }
                    loadAsset.AssetContent = memoryStream.ToArray();
                }

                return(true);
            }

            return(false);
        }
예제 #11
0
        public static bool MigrateAssetIfNeeded(ILogger log, string assetFullPath)
        {
            // Determine if asset was Yaml or not
            var assetFileExtension = Path.GetExtension(assetFullPath);

            if (assetFileExtension == null)
            {
                return(false);
            }

            assetFileExtension = assetFileExtension.ToLowerInvariant();

            var serializer = AssetSerializer.FindSerializer(assetFileExtension);

            if (!(serializer is AssetYamlSerializer))
            {
                return(false);
            }

            // We've got a Yaml asset, let's get expected and serialized versions
            var  serializedVersion = 0;
            var  expectedVersion   = 0;
            Type assetType;

            // Read from Yaml file the asset version and its type (to get expected version)
            // Note: It tries to read as few as possible (SerializedVersion is expected to be right after Id, so it shouldn't try to read further than that)
            using (var streamReader = new StreamReader(assetFullPath))
            {
                var yamlEventReader = new EventReader(new Parser(streamReader));

                // Skip header
                yamlEventReader.Expect <StreamStart>();
                yamlEventReader.Expect <DocumentStart>();
                var mappingStart = yamlEventReader.Expect <MappingStart>();

                var yamlSerializerSettings = YamlSerializer.GetSerializerSettings();
                var tagTypeRegistry        = yamlSerializerSettings.TagTypeRegistry;
                assetType = tagTypeRegistry.TypeFromTag(mappingStart.Tag);

                expectedVersion = AssetRegistry.GetFormatVersion(assetType);

                Scalar assetKey;
                while ((assetKey = yamlEventReader.Allow <Scalar>()) != null)
                {
                    // Only allow Id before SerializedVersion
                    if (assetKey.Value == "Id")
                    {
                        yamlEventReader.Skip();
                        continue;
                    }
                    if (assetKey.Value == "SerializedVersion")
                    {
                        serializedVersion = Convert.ToInt32(yamlEventReader.Expect <Scalar>().Value, CultureInfo.InvariantCulture);
                        break;
                    }
                }
            }

            if (serializedVersion > expectedVersion)
            {
                // Try to open an asset newer than what we support (probably generated by a newer Paradox)
                throw new InvalidOperationException(string.Format("Asset of type {0} has been serialized with newer version {1}, but only version {2} is supported. Was this asset created with a newer version of Paradox?", assetType, serializedVersion, expectedVersion));
            }

            if (serializedVersion < expectedVersion)
            {
                // Perform asset upgrade
                log.Info("{0} needs update, from version {0} to version {1}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);

                // Load the asset as a YamlNode object
                var input      = new StringReader(File.ReadAllText(assetFullPath));
                var yamlStream = new YamlStream();
                yamlStream.Load(input);
                var yamlRootNode = (YamlMappingNode)yamlStream.Documents[0].RootNode;

                // Check if there is any asset updater
                var assetUpdaterTypes = AssetRegistry.GetFormatVersionUpdaterTypes(assetType);
                if (assetUpdaterTypes == null)
                {
                    throw new InvalidOperationException(string.Format("Asset of type {0} should be updated from version {1} to {2}, but no asset migration path was found", assetType, serializedVersion, expectedVersion));
                }

                // Instantiate asset updaters
                var assetUpgraders = assetUpdaterTypes.Select(x => (IAssetUpgrader)Activator.CreateInstance(x)).ToArray();

                // TODO: Select best asset updater if more than one (need to check from what to what version they update, score, if multiple need to be chained, etc...)
                // I think it's better to wait for some actual scenarios to implement this right the first time
                if (assetUpgraders.Length != 1)
                {
                    throw new InvalidOperationException(string.Format("Asset of type {0} has multiple migration paths, but selecting the right one is not implemented yet.", assetType));
                }

                // Perform upgrade
                assetUpgraders[0].Upgrade(log, yamlRootNode);

                // Make sure asset is updated to latest version
                YamlNode serializedVersionNode;
                serializedVersion = 0;
                if (yamlRootNode.Children.TryGetValue(new YamlScalarNode("SerializedVersion"), out serializedVersionNode))
                {
                    serializedVersion = Convert.ToInt32(((YamlScalarNode)serializedVersionNode).Value);
                }

                if (serializedVersion != expectedVersion)
                {
                    throw new InvalidOperationException(string.Format("Asset of type {0} was migrated, but still its new version {1} doesn't match expected version {2}.", assetType, serializedVersion, expectedVersion));
                }

                var preferredIndent = YamlSerializer.GetSerializerSettings().PreferredIndent;

                // Save asset back to disk
                using (var streamWriter = new StreamWriter(assetFullPath))
                    yamlStream.Save(streamWriter, true, preferredIndent);

                return(true);
            }

            return(false);
        }