/// <summary>
        /// Deserializes the files.
        /// </summary>
        public LoadedDatabase Deserialize()
        {
            var deserializer = new DeserializerBuilder().Build();

            using var dbs = new StreamReader(Path.Combine(_inputDirectory, "info.yml"));
            var loadedDatabase = deserializer.Deserialize <LoadedDatabase>(dbs);
            var isX86          = _database.Options.Type == DatabaseType.X86Database;

            foreach (var loadedDatabaseClass in loadedDatabase.Classes)
            {
                var vltClass = new VltClass(loadedDatabaseClass.Name);

                foreach (var loadedDatabaseClassField in loadedDatabaseClass.Fields)
                {
                    var field = new VltClassField(
                        isX86 ? VLT32Hasher.Hash(loadedDatabaseClassField.Name) : VLT64Hasher.Hash(loadedDatabaseClassField.Name),
                        loadedDatabaseClassField.Name,
                        loadedDatabaseClassField.TypeName,
                        loadedDatabaseClassField.Flags,
                        loadedDatabaseClassField.Alignment,
                        loadedDatabaseClassField.Size,
                        loadedDatabaseClassField.MaxCount,
                        loadedDatabaseClassField.Offset);
                    // Handle static value
                    if (loadedDatabaseClassField.StaticValue != null)
                    {
                        field.StaticValue = ConvertSerializedValueToDataValue(_database.Options.GameId, _inputDirectory, vltClass, field, null,
                                                                              loadedDatabaseClassField.StaticValue);
                    }

                    vltClass.Fields.Add(field.Key, field);
                }

                _database.AddClass(vltClass);
            }

            foreach (var loadedDatabaseType in loadedDatabase.Types)
            {
                _database.Types.Add(new DatabaseTypeInfo {
                    Name = loadedDatabaseType.Name, Size = loadedDatabaseType.Size
                });
            }

            var collectionParentDictionary = new Dictionary <string, string>();
            var collectionDictionary       = new Dictionary <string, VltCollection>();
            var vaultsToSaveDictionary     = new Dictionary <string, List <Vault> >();
            var collectionsToBeAdded       = new List <VltCollection>();

            foreach (var file in loadedDatabase.Files)
            {
                file.LoadedVaults = new List <Vault>();

                var baseDirectory = Path.Combine(_inputDirectory, file.Group, file.Name);
                vaultsToSaveDictionary[file.Name] = new List <Vault>();
                foreach (var vault in file.Vaults)
                {
                    var vaultDirectory = Path.Combine(baseDirectory, vault).Trim();
                    var newVault       = new Vault(vault)
                    {
                        Database = _database, IsPrimaryVault = vault == "db"
                    };
                    if (Directory.Exists(vaultDirectory))
                    {
                        HashSet <string> trackedCollections = new HashSet <string>();

                        foreach (var dataFile in Directory.GetFiles(vaultDirectory, "*.yml"))
                        {
                            var className = Path.GetFileNameWithoutExtension(dataFile);
                            var vltClass  = _database.FindClass(className);

                            if (vltClass == null)
                            {
                                throw new InvalidDataException($"Unknown class: {className} ({dataFile})");
                            }

                            //#if DEBUG
                            //                        Debug.WriteLine("Processing class '{0}' in vault '{1}' (file: {2})", className, vault, dataFile);
                            //#else
                            //                        Console.WriteLine("Processing class '{0}' in vault '{1}' (file: {2})", className, vault, dataFile);
                            //#endif

                            using var vr = new StreamReader(dataFile);
                            var collections = deserializer.Deserialize <List <LoadedCollection> >(vr);

                            foreach (var loadedCollection in collections)
                            {
                                // BUG 16.02.2020: we have to do this to get around a YamlDotNet bug
                                if (loadedCollection.Name == null)
                                {
                                    loadedCollection.Name = "null";
                                }

                                foreach (var k in loadedCollection.Data.Keys.ToList().Where(k => loadedCollection.Data[k] == null))
                                {
                                    loadedCollection.Data[k] = "null";
                                }
                            }

                            var newCollections = new List <VltCollection>();

                            void AddCollectionsToList(ICollection <VltCollection> collectionList,
                                                      IEnumerable <LoadedCollection> collectionsToAdd)
                            {
                                if (collectionList == null)
                                {
                                    throw new Exception("collectionList should not be null!");
                                }
                                collectionsToAdd ??= new List <LoadedCollection>();

                                foreach (var loadedCollection in collectionsToAdd)
                                {
                                    var newVltCollection = new VltCollection(newVault, vltClass, loadedCollection.Name);

                                    foreach (var(key, value) in loadedCollection.Data)
                                    {
                                        newVltCollection.SetRawValue(key,
                                                                     ConvertSerializedValueToDataValue(_database.Options.GameId, vaultDirectory, vltClass, vltClass[key],
                                                                                                       newVltCollection, value));
                                    }

                                    collectionParentDictionary[newVltCollection.ShortPath] = loadedCollection.ParentName;
                                    collectionList.Add(newVltCollection);
                                    collectionDictionary[newVltCollection.ShortPath] = newVltCollection;
                                }
                            }

                            AddCollectionsToList(newCollections, collections);


                            foreach (var newCollection in newCollections)
                            {
                                if (!trackedCollections.Add(newCollection.ShortPath))
                                {
                                    throw new SerializedDatabaseLoaderException($"Duplicate collection found! Multiple collections at '{newCollection.ShortPath}' have been defined in your YML files.");
                                }

                                collectionsToBeAdded.Add(newCollection);
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("WARN: vault {0} has no folder; looked for {1}", vault, vaultDirectory);
                    }

                    vaultsToSaveDictionary[file.Name].Add(newVault);
                    _database.Vaults.Add(newVault);

                    file.LoadedVaults.Add(newVault);
                }
            }

            // dependency resolution
            var resolved   = new List <VaultDependencyNode>();
            var unresolved = new List <VaultDependencyNode>();

            foreach (var vault in _database.Vaults)
            {
                var vaultCollections     = collectionsToBeAdded.Where(c => c.Vault.Name == vault.Name).ToList();
                VaultDependencyNode node = new VaultDependencyNode(vault);

                foreach (var vaultCollection in vaultCollections)
                {
                    string parentKey = collectionParentDictionary[vaultCollection.ShortPath];

                    if (!string.IsNullOrEmpty(parentKey))
                    {
                        var parentCollection = collectionDictionary[$"{vaultCollection.Class.Name}/{parentKey}"];
                        if (parentCollection.Vault.Name != vault.Name)
                        {
                            node.AddEdge(new VaultDependencyNode(parentCollection.Vault));
                        }
                    }
                }

                ResolveDependencies(node, resolved, unresolved);

                Debug.WriteLine("Vault {0}: {1} collections", vault.Name, vaultCollections.Count);
            }

            resolved   = resolved.Distinct(VaultDependencyNode.VaultComparer).ToList();
            unresolved = unresolved.Distinct(VaultDependencyNode.VaultComparer).ToList();

            if (unresolved.Count != 0)
            {
                throw new SerializedDatabaseLoaderException("Cannot continue loading - unresolved vault dependencies");
            }

            foreach (var node in resolved)
            {
                var vault            = node.Vault;
                var vaultCollections = collectionsToBeAdded.Where(c => c.Vault.Name == vault.Name).ToList();

                Debug.WriteLine("Loading collections for vault {0} ({1})", vault.Name, vaultCollections.Count);

                foreach (var collection in vaultCollections)
                {
                    string parentKey = collectionParentDictionary[collection.ShortPath];

                    if (string.IsNullOrEmpty(parentKey))
                    {
                        // Add collection directly
                        _database.RowManager.AddCollection(collection);
                    }
                    else
                    {
                        var parentCollection = collectionDictionary[$"{collection.Class.Name}/{parentKey}"];
                        parentCollection.AddChild(collection);
                    }
                }
            }

            _loadedDatabase = loadedDatabase;

            return(loadedDatabase);
        }
        /// <summary>
        /// Serializes the database.
        /// </summary>
        /// <param name="files">The files that were loaded.</param>
        public void Serialize(IEnumerable <LoadedDatabaseFile> files)
        {
            var loadedDatabase = new LoadedDatabase
            {
                Classes = new List <LoadedDatabaseClass>(),
                Files   = new List <LoadedDatabaseFile>(),
                Types   = new List <LoadedTypeInfo>()
            };

            loadedDatabase.Files.AddRange(files);

            foreach (var databaseType in _database.Types)
            {
                loadedDatabase.Types.Add(new LoadedTypeInfo
                {
                    Name = databaseType.Name,
                    Size = databaseType.Size
                });
            }

            foreach (var databaseClass in _database.Classes)
            {
                var loadedDatabaseClass = new LoadedDatabaseClass
                {
                    Name   = databaseClass.Name,
                    Fields = new List <LoadedDatabaseClassField>()
                };

                loadedDatabaseClass.Fields.AddRange(databaseClass.Fields.Values.Select(field =>
                                                                                       new LoadedDatabaseClassField
                {
                    Name        = field.Name,
                    TypeName    = field.TypeName,
                    Alignment   = field.Alignment,
                    Flags       = field.Flags,
                    MaxCount    = field.MaxCount,
                    Size        = field.Size,
                    Offset      = field.Offset,
                    StaticValue = ConvertDataValueToSerializedValue(_outputDirectory, null, field, field.StaticValue)
                }));

                loadedDatabase.Classes.Add(loadedDatabaseClass);
            }

            var serializerBuilder = new SerializerBuilder();
            var serializer        = serializerBuilder.Build();

            using var sw = new StreamWriter(Path.Combine(_outputDirectory, "info.yml"));
            serializer.Serialize(sw, loadedDatabase);

            foreach (var loadedDatabaseFile in loadedDatabase.Files)
            {
                var baseDirectory = Path.Combine(_outputDirectory, loadedDatabaseFile.Group, loadedDatabaseFile.Name);
                Directory.CreateDirectory(baseDirectory);

                foreach (var vault in loadedDatabaseFile.LoadedVaults)
                {
                    var vaultDirectory = Path.Combine(baseDirectory, vault.Name).Trim();
                    Directory.CreateDirectory(vaultDirectory);

                    // Problem: Gameplay data is separated into numerous vaults, so we can't easily construct a proper hierarchy
                    // Solution: Store the name of the parent node instead of having an array of children.

                    foreach (var collectionGroup in _database.RowManager.GetCollectionsInVault(vault)
                             .GroupBy(v => v.Class.Name))
                    {
                        var loadedCollections = new List <LoadedCollection>();
                        AddLoadedCollections(vaultDirectory, loadedCollections, collectionGroup);

                        using var vw = new StreamWriter(Path.Combine(vaultDirectory, collectionGroup.Key + ".yml"));
                        serializer.Serialize(vw, loadedCollections);
                    }
                }
            }
        }