Beispiel #1
0
        public DBDefinition Read(Stream stream, bool validate = false)
        {
            var reader = new StreamReader(stream);
            var columnDefinitionDictionary = new Dictionary <string, ColumnDefinition>();

            var lines = reader.ReadLines();

            reader.Close();
            reader.Dispose();

            var lineNumber = 0;

            if (lines[0].StartsWith("COLUMNS"))
            {
                lineNumber++;
                while (true)
                {
                    var line = lines[lineNumber++];

                    // Column definitions are done after encountering a newline
                    if (string.IsNullOrWhiteSpace(line))
                    {
                        break;
                    }

                    // Create a new column definition to store information in
                    var columnDefinition = new ColumnDefinition();

                    /* TYPE READING */
                    // List of valid types, uint should be removed soon-ish
                    var validTypes = new List <string> {
                        "uint", "int", "float", "string", "locstring"
                    };

                    // Check if line has a space in case someone didn't assign a type to a column name
                    if (!line.Contains(" "))
                    {
                        throw new Exception("Line " + line + " does not contain a space between type and column name!");
                    }

                    // Read line up to space (end of type) or < (foreign key)
                    var type = line.Substring(0, line.IndexOfAny(new char[] { ' ', '<' }));

                    // Check if type is valid, throw exception if not!
                    if (!validTypes.Contains(type))
                    {
                        throw new Exception("Invalid type: " + type + " on line " + lineNumber);
                    }
                    else
                    {
                        columnDefinition.type = type;
                    }

                    /* FOREIGN KEY READING */
                    // Only read foreign key if foreign key identifier is found right after type (it could also be in comments)
                    if (line.StartsWith(type + "<"))
                    {
                        // Read foreign key info between < and > without < and > in result, then split on :: to separate table and field
                        var foreignKey = line.Substring(line.IndexOf('<') + 1, line.IndexOf('>') - line.IndexOf('<') - 1).Split(new string[] { "::" }, StringSplitOptions.None);

                        // There should only be 2 values in foreignKey (table and col)
                        if (foreignKey.Length != 2)
                        {
                            throw new Exception("Invalid foreign key length: " + foreignKey.Length);
                        }
                        else
                        {
                            columnDefinition.foreignTable  = foreignKey[0];
                            columnDefinition.foreignColumn = foreignKey[1];
                        }
                    }

                    /* NAME READING */
                    var name = "";
                    // If there's only one space on the line at the same locaiton as the first one, assume a simple line like "uint ID", this can be better
                    if (line.LastIndexOf(' ') == line.IndexOf(' '))
                    {
                        name = line.Substring(line.IndexOf(' ') + 1);
                    }
                    else
                    {
                        // Location of first space (after type)
                        var start = line.IndexOf(' ');

                        // Second space (after name)
                        var end = line.IndexOf(' ', start + 1) - start - 1;

                        name = line.Substring(start + 1, end);
                    }

                    // If name ends in ? it's unverified
                    if (name.EndsWith("?"))
                    {
                        columnDefinition.verified = false;
                        name = name.Remove(name.Length - 1);
                    }
                    else
                    {
                        columnDefinition.verified = true;
                    }

                    /* COMMENT READING */
                    if (line.Contains("//"))
                    {
                        columnDefinition.comment = line.Substring(line.IndexOf("//") + 2).Trim();
                    }

                    // Add to dictionary
                    if (columnDefinitionDictionary.ContainsKey(name))
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Collision with existing column name while adding new column name! Skipping..");
                        Console.ResetColor();
                    }
                    else
                    {
                        columnDefinitionDictionary.Add(name, columnDefinition);
                    }
                }
            }
            else
            {
                throw new Exception("File does not start with column definitions!");
            }

            // There will be less comments from this point on, stuff used in above code is mostly repeated

            var versionDefinitions = new List <VersionDefinitions>();

            var definitions  = new List <Definition>();
            var layoutHashes = new List <string>();
            var comment      = "";
            var builds       = new List <Build>();
            var buildRanges  = new List <BuildRange>();

            for (var i = lineNumber; i < lines.Count; i++)
            {
                var line = lines[i];

                if (string.IsNullOrWhiteSpace(line))
                {
                    versionDefinitions.Add(
                        new VersionDefinitions()
                    {
                        builds       = builds.ToArray(),
                        buildRanges  = buildRanges.ToArray(),
                        layoutHashes = layoutHashes.ToArray(),
                        comment      = comment,
                        definitions  = definitions.ToArray()
                    }
                        );

                    definitions  = new List <Definition>();
                    layoutHashes = new List <string>();
                    comment      = "";
                    builds       = new List <Build>();
                    buildRanges  = new List <BuildRange>();
                }

                if (line.StartsWith("LAYOUT"))
                {
                    var splitLayoutHashes = line.Remove(0, 7).Split(new string[] { ", " }, StringSplitOptions.None);
                    layoutHashes.AddRange(splitLayoutHashes);
                }

                if (line.StartsWith("BUILD"))
                {
                    var splitBuilds = line.Remove(0, 6).Split(new string[] { ", " }, StringSplitOptions.None);
                    foreach (var splitBuild in splitBuilds)
                    {
                        if (splitBuild.Contains("-"))
                        {
                            var splitRange = splitBuild.Split('-');
                            buildRanges.Add(
                                new BuildRange(new Build(splitRange[0]), new Build(splitRange[1]))
                                );
                        }
                        else
                        {
                            var build = new Build(splitBuild);
                            builds.Add(build);
                        }
                    }
                }

                if (line.StartsWith("COMMENT"))
                {
                    comment = line.Substring(7).Trim();
                }

                if (!line.StartsWith("LAYOUT") && !line.StartsWith("BUILD") && !line.StartsWith("COMMENT") && !string.IsNullOrWhiteSpace(line))
                {
                    var definition = new Definition();

                    // Default to everything being inline
                    definition.isNonInline = false;

                    if (line.Contains("$"))
                    {
                        var annotationStart = line.IndexOf("$");
                        var annotationEnd   = line.IndexOf("$", 1);

                        var annotations = new List <string>(line.Substring(annotationStart + 1, annotationEnd - annotationStart - 1).Split(','));

                        if (annotations.Contains("id"))
                        {
                            definition.isID = true;
                        }

                        if (annotations.Contains("noninline"))
                        {
                            definition.isNonInline = true;
                        }

                        if (annotations.Contains("relation"))
                        {
                            definition.isRelation = true;
                        }

                        line = line.Remove(annotationStart, annotationEnd + 1);
                    }

                    if (line.Contains("<"))
                    {
                        var size = line.Substring(line.IndexOf('<') + 1, line.IndexOf('>') - line.IndexOf('<') - 1);

                        if (size[0] == 'u')
                        {
                            definition.isSigned = false;
                            definition.size     = int.Parse(size.Replace("u", ""));
                        }
                        else
                        {
                            definition.isSigned = true;
                            definition.size     = int.Parse(size);
                        }

                        line = line.Remove(line.IndexOf('<'), line.IndexOf('>') - line.IndexOf('<') + 1);
                    }

                    if (line.Contains("["))
                    {
                        int.TryParse(line.Substring(line.IndexOf('[') + 1, line.IndexOf(']') - line.IndexOf('[') - 1), out definition.arrLength);
                        line = line.Remove(line.IndexOf('['), line.IndexOf(']') - line.IndexOf('[') + 1);
                    }

                    if (line.Contains("//"))
                    {
                        definition.comment = line.Substring(line.IndexOf("//") + 2).Trim();
                        line = line.Remove(line.IndexOf("//")).Trim();
                    }

                    definition.name = line;

                    // Check if this column name is known in column definitions, if not throw exception
                    if (!columnDefinitionDictionary.ContainsKey(definition.name))
                    {
                        throw new KeyNotFoundException("Unable to find " + definition.name + " in column definitions!");
                    }
                    else
                    {
                        // Temporary unsigned format update conversion code
                        if (columnDefinitionDictionary[definition.name].type == "uint")
                        {
                            definition.isSigned = false;
                        }
                    }

                    definitions.Add(definition);
                }

                if (lines.Count == (i + 1))
                {
                    versionDefinitions.Add(
                        new VersionDefinitions()
                    {
                        builds       = builds.ToArray(),
                        buildRanges  = buildRanges.ToArray(),
                        layoutHashes = layoutHashes.ToArray(),
                        comment      = comment,
                        definitions  = definitions.ToArray()
                    }
                        );
                }
            }

            // Validation is optional!
            if (validate)
            {
                var newColumnDefDict = new Dictionary <string, ColumnDefinition>();
                foreach (var column in columnDefinitionDictionary)
                {
                    newColumnDefDict.Add(column.Key, column.Value);
                }

                var seenBuilds       = new List <Build>();
                var seenLayoutHashes = new List <string>();

                foreach (var column in columnDefinitionDictionary)
                {
                    var found = false;

                    foreach (var version in versionDefinitions)
                    {
                        foreach (var definition in version.definitions)
                        {
                            if (column.Key == definition.name)
                            {
                                if (definition.name == "ID" && !definition.isID)
                                {
                                    Console.ForegroundColor = ConsoleColor.Yellow;
                                    Console.WriteLine(definition.name + " is called ID and might be a primary key.");
                                    Console.ResetColor();
                                }
                                found = true;
                                break;
                            }
                        }
                    }

                    if (!found)
                    {
                        Console.WriteLine("Column definition " + column.Key + " is never used in version definitions!");
                        newColumnDefDict.Remove(column.Key);
                    }
                }

                columnDefinitionDictionary = newColumnDefDict;

                foreach (var version in versionDefinitions)
                {
                    foreach (var build in version.builds)
                    {
                        if (seenBuilds.Contains(build))
                        {
                            throw new Exception("Build " + build.ToString() + " is already defined!");
                        }
                        else
                        {
                            seenBuilds.Add(build);
                        }
                    }

                    foreach (var layoutHash in version.layoutHashes)
                    {
                        if (seenLayoutHashes.Contains(layoutHash))
                        {
                            throw new Exception("Layout hash " + layoutHash + " is already defined!");
                        }
                        else
                        {
                            seenLayoutHashes.Add(layoutHash);
                        }

                        if (layoutHash.Length != 8)
                        {
                            throw new Exception("Layout hash \"" + layoutHash + "\" is wrong length");
                        }
                    }

                    // Check if int/uint columns have sizes set or the other way around
                    foreach (var definition in version.definitions)
                    {
                        if ((columnDefinitionDictionary[definition.name].type == "int" || columnDefinitionDictionary[definition.name].type == "uint") && definition.size == 0)
                        {
                            throw new Exception("Version definition " + definition.name + " is an int/uint but is missing size!");
                        }

                        if ((columnDefinitionDictionary[definition.name].type != "int" && columnDefinitionDictionary[definition.name].type != "uint") && definition.size != 0)
                        {
                            throw new Exception("Version definition " + definition.name + " is NOT an int/uint but has size!");
                        }
                    }

                    if (version.definitions.GroupBy(n => n.name).Any(c => c.Count() > 1))
                    {
                        throw new Exception("Version definitions contains multiple columns of the same name!");
                    }
                }

                for (var i = 0; i < versionDefinitions.Count; i++)
                {
                    for (var j = 0; j < versionDefinitions.Count; j++)
                    {
                        if (i == j)
                        {
                            continue;         // Do not compare same entry
                        }
                        for (var b = 0; b < versionDefinitions[i].buildRanges.Length; b++)
                        {
                            for (var c = 0; c < versionDefinitions[j].builds.Length; c++)
                            {
                                if (versionDefinitions[i].buildRanges[b].Contains(versionDefinitions[j].builds[c]))
                                {
                                    throw new Exception("Build " + versionDefinitions[j].builds[c] + " conflicts with " + versionDefinitions[i].buildRanges[b] + "!");
                                }
                            }

                            for (var c = 0; c < versionDefinitions[j].buildRanges.Length; c++)
                            {
                                if (versionDefinitions[i].buildRanges[b].Contains(versionDefinitions[j].buildRanges[c].minBuild) || versionDefinitions[i].buildRanges[b].Contains(versionDefinitions[j].buildRanges[c].maxBuild))
                                {
                                    throw new Exception("Build " + versionDefinitions[j].buildRanges[c] + " conflicts with " + versionDefinitions[i].buildRanges[b] + "!");
                                }
                            }
                        }

                        if (versionDefinitions[i].definitions.SequenceEqual(versionDefinitions[j].definitions))
                        {
                            if (versionDefinitions[i].layoutHashes.Length > 0 && versionDefinitions[j].layoutHashes.Length > 0 && !versionDefinitions[i].layoutHashes.SequenceEqual(versionDefinitions[j].layoutHashes))
                            {
                                Console.ForegroundColor = ConsoleColor.Yellow;
                                Console.WriteLine("DBD file has 2 identical version definitions (" + (i + 1) + " and " + (j + 1) + ") but two different layouthashes, ignoring...");
                                Console.ResetColor();
                            }
                            else
                            {
                                throw new Exception("DBD file has 2 identical version definitions (" + (i + 1) + " and " + (j + 1) + ")!");
                            }
                        }
                    }
                }
            }

            return(new DBDefinition
            {
                columnDefinitions = columnDefinitionDictionary,
                versionDefinitions = versionDefinitions.ToArray()
            });
        }
Beispiel #2
0
 public static string BuildToString(Build build)
 {
     return(build.ToString());
 }