Esempio n. 1
0
        public override void Execute(ModScriptDatabaseHelper database)
        {
            VltCollection srcCollection = GetCollection(database, ClassName, SourceCollectionName);
            VltCollection dstCollection = GetCollection(database, ClassName, DestinationCollectionName);
            Dictionary <VltClassField, VLTBaseType> values = new Dictionary <VltClassField, VLTBaseType>();

            if ((Options & CopyOptions.Base) != 0)
            {
                foreach (var baseField in srcCollection.Class.BaseFields)
                {
                    values.Add(baseField,
                               ValueCloningUtils.CloneValue(database.Database, srcCollection.GetRawValue(baseField.Name), srcCollection.Class,
                                                            baseField, dstCollection));
                }
            }

            if ((Options & CopyOptions.Optional) != 0)
            {
                foreach (var(key, value) in srcCollection.GetData())
                {
                    var field = srcCollection.Class[key];

                    if (!field.IsInLayout)
                    {
                        values.Add(field, ValueCloningUtils.CloneValue(database.Database, value, srcCollection.Class, field, dstCollection));
                    }
                }
            }

            // base will always overwrite
            // optional by itself will copy anything that doesn't exist
            // optional + overwrite will copy nonexistent fields and overwrite the other ones(optional only)
            if ((Options & CopyOptions.Base) != 0)
            {
                foreach (var(key, value) in values)
                {
                    if (key.IsInLayout)
                    {
                        dstCollection.SetRawValue(key.Name, value);
                    }
                }
            }

            if ((Options & CopyOptions.Optional) != 0)
            {
                foreach (var(field, value) in values)
                {
                    if (!field.IsInLayout && (!dstCollection.HasEntry(field.Name) || (Options & CopyOptions.OverwriteOptional) != 0))
                    {
                        dstCollection.SetRawValue(field.Name, value);
                    }
                }
            }
        }
        public override void Execute(ModScriptDatabaseHelper database)
        {
            VltCollection collection = GetCollection(database, ClassName, CollectionName);
            VltClassField field      = collection.Class[FieldName];

            if (field.IsInLayout)
            {
                throw new InvalidDataException($"add_field failed because field '{field.Name}' is a base field");
            }

            if (collection.HasEntry(field.Name))
            {
                throw new InvalidDataException($"add_field failed because collection '{collection.ShortPath}' already has field '{field.Name}'");
            }

            var vltBaseType = TypeRegistry.CreateInstance(database.Database.Options.GameId, collection.Class, field, collection);

            if (vltBaseType is VLTArrayType array)
            {
                if (ArrayCapacity > field.MaxCount)
                {
                    throw new ModScriptCommandExecutionException(
                              $"Cannot add field {ClassName}[{FieldName}] with capacity beyond maximum (requested {ArrayCapacity} but limit is {field.MaxCount})");
                }

                array.Capacity      = ArrayCapacity;
                array.ItemAlignment = field.Alignment;
                array.FieldSize     = field.Size;
                array.Items         = new List <VLTBaseType>();

                for (var i = 0; i < ArrayCapacity; i++)
                {
                    array.Items.Add(TypeRegistry.ConstructInstance(array.ItemType, collection.Class, field, collection));
                }
            }

            collection.SetRawValue(field.Name, vltBaseType);
        }
        /// <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);
        }