예제 #1
0
        public static void WriteParams(SQLiteConnection con, string outputPath, bool overwriteOutputFiles)
        {
            // Writing a parambnd
            // Need to construct our BND3 files based on what's in our DB.
            // This is a kludge to create a mapping (filename -> (source_path, BND)).
            var bnds = new Dictionary <string, KeyValuePair <string, BND3> >();

            // First thing to do is get our basic BND file setup.
            using (var cmd = new SQLiteCommand(@"SELECT * FROM 'bnd_metadata'", con))
            {
                var reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    var bnd = new BND3
                    {
                        BigEndian    = reader.GetBoolean(reader.GetOrdinal(@"big_endian")),
                        BitBigEndian = reader.GetBoolean(reader.GetOrdinal(@"bit_big_endian")),
                        Compression  = (DCX.Type)Enum.Parse(typeof(DCX.Type), reader.GetString(reader.GetOrdinal(@"compression"))),
                        Format       = (Binder.Format)reader.GetInt64(reader.GetOrdinal(@"format")),
                        Unk18        = reader.GetInt32(reader.GetOrdinal(@"unk18")),
                        Version      = reader.GetString(reader.GetOrdinal(@"version")),
                        Files        = new List <BinderFile>()
                    };
                    var filename = reader.GetString(reader.GetOrdinal(@"filename"));
                    bnds.Add(Path.GetFileName(filename), new KeyValuePair <string, BND3>(filename, bnd));
                }
            }

            // Get our list of files. We'll grab the contents afterwards.
            // Note that it's a List because there can be multiple files associated with a given ParamType.
            var files = new Dictionary <string, KeyValuePair <string, List <BinderFile> > >();

            using (var cmd = new SQLiteCommand(@"SELECT * FROM 'bnd_contents'", con))
            {
                var reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    var source_file = reader.GetString(reader.GetOrdinal(@"source_file"));
                    var file        = new BinderFile
                    {
                        ID              = reader.GetInt32(reader.GetOrdinal(@"file_id")),
                        Name            = reader.GetString(reader.GetOrdinal(@"name")),
                        Flags           = (Binder.FileFlags)reader.GetInt64(reader.GetOrdinal(@"flags")),
                        CompressionType = (DCX.Type)System.Enum.Parse(typeof(DCX.Type), reader.GetString(reader.GetOrdinal(@"compression_type")))
                    };

                    var paramType = reader.GetString(reader.GetOrdinal("param_type"));

                    // Add the file to both our list of files in the appropriate BND and also to our dictionary
                    // so that we can continue building it out.
                    bnds[source_file].Value.Files.Add(file);
                    if (files.ContainsKey(Path.GetFileNameWithoutExtension(file.Name)))
                    {
                        var dictValue = files.TryGetValue(Path.GetFileNameWithoutExtension(file.Name), out KeyValuePair <string, List <BinderFile> > value) ? value :
                                        new KeyValuePair <string, List <BinderFile> >(paramType, new List <BinderFile>());
                        dictValue.Value.Add(file);
                    }
                    else
                    {
                        var dictValue = new KeyValuePair <string, List <BinderFile> >(paramType, new List <BinderFile>()
                        {
                            file
                        });
                        files.Add(Path.GetFileNameWithoutExtension(file.Name), dictValue);
                    }
                }
            }

            // Get all of our PARAMDEFs
            Dictionary <string, PARAMDEF> paramTypeToParamDef = new Dictionary <string, PARAMDEF>();

            using (var cmd = new SQLiteCommand(@"SELECT * FROM 'paramdef_metadata';", con))
                using (var fieldsCmd = new SQLiteCommand(@"SELECT * FROM 'paramdef_fields' WHERE param_type=$param_type;", con))
                {
                    var reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        PARAMDEF paramdef = new PARAMDEF
                        {
                            BigEndian     = reader.GetBoolean(reader.GetOrdinal(@"big_endian")),
                            Compression   = (DCX.Type)Enum.Parse(typeof(DCX.Type), reader.GetString(reader.GetOrdinal(@"compression"))),
                            ParamType     = reader.GetString(reader.GetOrdinal(@"param_type")),
                            Unicode       = reader.GetBoolean(reader.GetOrdinal(@"unicode")),
                            DataVersion   = reader.GetInt16(reader.GetOrdinal(@"data_version")),
                            FormatVersion = reader.GetInt16(reader.GetOrdinal(@"format_version"))
                        };
                        paramTypeToParamDef.Add(paramdef.ParamType, paramdef);
                    }
                }

            using (var cmd = new SQLiteCommand(@"SELECT * FROM 'paramdef_fields' WHERE param_type=$param_type;", con))
            {
                foreach (KeyValuePair <string, PARAMDEF> keyValue in paramTypeToParamDef)
                {
                    // Get all the fields for our paramdef
                    AddParamToCommand(cmd, @"$param_type", keyValue.Key);
                    var fieldReader = cmd.ExecuteReader();
                    var fields      = new List <Field>();
                    while (fieldReader.Read())
                    {
                        var descOrdinal = fieldReader.GetOrdinal(@"description");
                        var field       = new Field
                        {
                            ArrayLength = fieldReader.GetInt32(fieldReader.GetOrdinal(@"array_length")),
                            BitSize     = fieldReader.GetInt32(fieldReader.GetOrdinal(@"bit_size")),
                            Default     = fieldReader.GetFloat(fieldReader.GetOrdinal(@"default")),
                            // Description can be NULL. Need to check. Sigh.
                            Description   = fieldReader.IsDBNull(descOrdinal) ? null : fieldReader.GetFieldValue <string>(descOrdinal),
                            DisplayFormat = fieldReader.GetString(fieldReader.GetOrdinal(@"display_format")),
                            DisplayName   = fieldReader.GetString(fieldReader.GetOrdinal(@"display_name")),
                            DisplayType   = (DefType)System.Enum.Parse(typeof(DefType), fieldReader.GetString(fieldReader.GetOrdinal(@"display_type"))),
                            EditFlags     = (EditFlags)fieldReader.GetInt64(fieldReader.GetOrdinal(@"edit_flags")),
                            Increment     = fieldReader.GetFloat(fieldReader.GetOrdinal(@"increment")),
                            InternalName  = fieldReader.GetString(fieldReader.GetOrdinal(@"internal_name")),
                            InternalType  = fieldReader.GetString(fieldReader.GetOrdinal(@"internal_type")),
                            Maximum       = fieldReader.GetFloat(fieldReader.GetOrdinal(@"maximum")),
                            Minimum       = fieldReader.GetFloat(fieldReader.GetOrdinal(@"minimum")),
                            SortID        = fieldReader.GetInt32(fieldReader.GetOrdinal(@"sort_id"))
                        };

                        fields.Add(field);
                    }
                    keyValue.Value.Fields = fields;
                    var exc = new Exception();
                    if (!keyValue.Value.Validate(out exc))
                    {
                        throw exc;
                    }
                    fieldReader.Close();
                }
            }

            // Now we need to grab our contents for each file.
            foreach (KeyValuePair <string, KeyValuePair <string, List <BinderFile> > > entry in files)
            {
                // Want to iterate through each file. Keep in mind multiple tables can have same ParamType, so we can't loop via ParamType.
                // e.g. DeS AtkParam_Npc and AtkParam_Pc
                foreach (BinderFile file in entry.Value.Value)
                {
                    //var tableName = Path.GetFileNameWithoutExtension(file.Name);
                    var tableName = entry.Key;
                    Console.WriteLine("Reading from: " + tableName);
                    using (var cmd = new SQLiteCommand(@"SELECT * FROM '" + tableName + "';", con))
                        using (var metadataCmd = new SQLiteCommand(@"SELECT * FROM param_metadata WHERE param_type = $param_type", con))
                        {
                            var paramDef  = paramTypeToParamDef[entry.Value.Key];
                            var paramFile = new PARAM();
                            paramFile.ParamType = entry.Value.Key;

                            AddParamToCommand(metadataCmd, @"$param_type", entry.Value.Key);
                            var metadataReader = metadataCmd.ExecuteReader();
                            while (metadataReader.Read())
                            {
                                paramFile.BigEndian   = metadataReader.GetBoolean(metadataReader.GetOrdinal(@"big_endian"));
                                paramFile.Compression = (DCX.Type)Enum.Parse(typeof(DCX.Type), metadataReader.GetString(metadataReader.GetOrdinal(@"compression")));
                                paramFile.Format2D    = (PARAM.FormatFlags1)Enum.Parse(typeof(PARAM.FormatFlags1), metadataReader.GetString(metadataReader.GetOrdinal(@"format2d")));
                                paramFile.Format2E    = (PARAM.FormatFlags2)Enum.Parse(typeof(PARAM.FormatFlags2), metadataReader.GetString(metadataReader.GetOrdinal(@"format2e")));
                                byte[] buf = new byte[1];
                                metadataReader.GetBytes(metadataReader.GetOrdinal("paramdef_format_version"), 0, buf, 0, 1);
                                paramFile.ParamdefFormatVersion = buf[0];
                                paramFile.Unk06 = metadataReader.GetInt16(metadataReader.GetOrdinal(@"unk06"));
                                paramFile.ParamdefDataVersion = metadataReader.GetInt16(metadataReader.GetOrdinal(@"paramdef_data_version"));
                            }

                            var reader = cmd.ExecuteReader();
                            paramFile.Rows = new List <PARAM.Row>();
                            while (reader.Read())
                            {
                                var id = reader.GetInt32(reader.GetOrdinal(@"id"));
                                // Description can be NULL
                                var descOrdinal = reader.GetOrdinal(@"description");
                                var description = reader.IsDBNull(descOrdinal) ? null : reader.GetFieldValue <string>(descOrdinal);
                                var row         = new PARAM.Row(id, description, paramDef);
                                foreach (Field field in paramDef.Fields)
                                {
                                    var name = field.InternalName;
                                    // Not using InternalType. I don't know the complete set of raw strings across all games.
                                    // It would be better to use not be tied to a display field. For some value of "better".
                                    var type = field.DisplayType;

                                    switch (type)
                                    {
                                    // Padding case
                                    case DefType.dummy8:
                                        int length = field.ArrayLength;
                                        if (field.BitSize == -1)
                                        {
                                            row[name].Value = Enumerable.Repeat((byte)0, length).ToArray();
                                        }
                                        else
                                        {
                                            row[name].Value = 0;
                                        }
                                        break;

                                    // All the integer cases
                                    case DefType.s8:
                                    case DefType.s16:
                                    case DefType.s32:
                                    case DefType.u8:
                                    case DefType.u16:
                                    case DefType.u32:
                                        row[name].Value = reader.GetInt32(reader.GetOrdinal(name));
                                        break;

                                    // Float cases
                                    case DefType.f32:
                                        row[name].Value = reader.GetFloat(reader.GetOrdinal(name));
                                        break;

                                    // String case
                                    case DefType.fixstr:
                                    case DefType.fixstrW:
                                        row[name].Value = reader.GetString(reader.GetOrdinal(name));
                                        break;
                                    }
                                }

                                paramFile.Rows.Add(row);
                            }

                            // Don't apply carefully. We don't have the ability to set the DetectedSize. It only occurs on Read
                            paramFile.ApplyParamdef(paramDef);
                            var exc = new Exception();
                            if (!paramFile.Validate(out exc))
                            {
                                Console.WriteLine("Failed with exception: " + exc);
                            }

                            file.Bytes = paramFile.Write();
                        }
                }
            }

            foreach (KeyValuePair <string, KeyValuePair <string, BND3> > entry in bnds)
            {
                // Default to writing the original file.
                // If output path is defined, put everything there.
                var outputFile = entry.Value.Key;
                if (outputPath != null)
                {
                    outputFile = outputPath + Path.DirectorySeparatorChar + entry.Key;
                    Console.WriteLine("Output current parambnd.dcx: " + outputFile);
                }

                if (!File.Exists(outputFile) || overwriteOutputFiles)
                {
                    entry.Value.Value.Write(outputFile);
                }
                else
                {
                    // Backup the eisting file before writing.
                    // Just append the unix time and ".bak" to avoid managing whole sets of backup nonsense.
                    var    unixTime   = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
                    string backupFile = outputFile + "." + unixTime + ".bak";
                    Console.WriteLine("Collision found. Not overwriting. Moving original file to backup at: " + backupFile);
                    File.Move(outputFile, backupFile);
                    entry.Value.Value.Write(outputFile);
                }
            }
        }