mirror of
https://github.com/Ryujinx/Opentk.git
synced 2024-12-25 07:45:35 +00:00
158f1af9d3
Added check for the existence of override elements before trying to use them (avoids potential null reference exception). Regenerated bindings using the latest version of the generator.
398 lines
13 KiB
C#
398 lines
13 KiB
C#
#region --- License ---
|
|
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
|
|
* See license.txt for license info
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Xml.XPath;
|
|
|
|
namespace Bind.Structures
|
|
{
|
|
#region class Enum
|
|
|
|
public class Enum
|
|
{
|
|
internal static EnumCollection GLEnums = new EnumCollection();
|
|
internal static EnumCollection AuxEnums = new EnumCollection();
|
|
|
|
static StringBuilder translator = new StringBuilder();
|
|
string _name, _type;
|
|
static bool enumsLoaded;
|
|
|
|
#region Initialize
|
|
|
|
internal static void Initialize(string enumFile, string enumextFile, string auxFile)
|
|
{
|
|
Initialize(enumFile, enumextFile);
|
|
|
|
if (!String.IsNullOrEmpty(auxFile))
|
|
using (StreamReader sr = new StreamReader(Path.Combine(Settings.InputPath, auxFile)))
|
|
{
|
|
AuxEnums = MainClass.Generator.ReadEnums(sr);
|
|
}
|
|
}
|
|
|
|
internal static void Initialize(string enumFile, string enumextFile)
|
|
{
|
|
if (!enumsLoaded)
|
|
{
|
|
if (!String.IsNullOrEmpty(enumFile))
|
|
{
|
|
using (StreamReader sr = Utilities.OpenSpecFile(Settings.InputPath, enumFile))
|
|
{
|
|
GLEnums = MainClass.Generator.ReadEnums(sr);
|
|
}
|
|
}
|
|
|
|
if (!String.IsNullOrEmpty(enumextFile))
|
|
{
|
|
using (StreamReader sr = Utilities.OpenSpecFile(Settings.InputPath, enumextFile))
|
|
{
|
|
foreach (Enum e in MainClass.Generator.ReadEnums(sr).Values)
|
|
{
|
|
//enums.Add(e.Name, e);
|
|
Utilities.Merge(GLEnums, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
enumsLoaded = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
public Enum()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Members
|
|
|
|
// Returns true if the enum contains a collection of flags, i.e. 1, 2, 4, 8, ...
|
|
public bool IsFlagCollection
|
|
{
|
|
get
|
|
{
|
|
// It seems that all flag collections contain "Mask" in their names.
|
|
// This looks like a heuristic, but it holds 100% in practice
|
|
// (checked all enums to make sure).
|
|
return Name.Contains("Mask");
|
|
}
|
|
}
|
|
|
|
#region public string Name
|
|
|
|
public string Name
|
|
{
|
|
get { return _name ?? ""; }
|
|
set { _name = value; }
|
|
}
|
|
|
|
// Typically 'long' or 'int'. Default is 'int'.
|
|
public string Type
|
|
{
|
|
get { return String.IsNullOrEmpty(_type) ? "int" : _type; }
|
|
set { _type = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ConstantCollection
|
|
|
|
Dictionary<string, Constant> _constant_collection = new Dictionary<string, Constant>();
|
|
|
|
public IDictionary<string, Constant> ConstantCollection
|
|
{
|
|
get { return _constant_collection; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TranslateName
|
|
|
|
// Translate the constant's name to match .Net naming conventions
|
|
public static string TranslateName(string name)
|
|
{
|
|
if (String.IsNullOrEmpty(name))
|
|
return name;
|
|
|
|
if (Utilities.Keywords.Contains(name))
|
|
return name;
|
|
|
|
translator.Remove(0, translator.Length); // Trick to avoid allocating a new StringBuilder.
|
|
|
|
if ((Settings.Compatibility & Settings.Legacy.NoAdvancedEnumProcessing) == Settings.Legacy.None)
|
|
{
|
|
// Detect if we just passed a '_' or a number and make the next char uppercase.
|
|
bool is_after_underscore_or_number = true;
|
|
// Detect if previous character was uppercase, and turn the current one to lowercase.
|
|
bool is_previous_uppercase = false;
|
|
|
|
foreach (char c in name)
|
|
{
|
|
char char_to_add;
|
|
if (c == '_' || c == '-')
|
|
is_after_underscore_or_number = true;
|
|
else
|
|
{
|
|
if (Char.IsDigit(c))
|
|
is_after_underscore_or_number = true;
|
|
|
|
char_to_add = is_after_underscore_or_number ? Char.ToUpper(c) :
|
|
is_previous_uppercase ? Char.ToLower(c) : c;
|
|
translator.Append(char_to_add);
|
|
|
|
is_previous_uppercase = Char.IsUpper(c);
|
|
is_after_underscore_or_number = false;
|
|
}
|
|
}
|
|
|
|
translator[0] = Char.ToUpper(translator[0]);
|
|
}
|
|
else
|
|
translator.Append(name);
|
|
|
|
translator.Replace("Pname", "PName");
|
|
translator.Replace("SRgb", "Srgb");
|
|
|
|
string ret = translator.ToString();
|
|
if (ret.StartsWith(Settings.EnumPrefix))
|
|
return ret.Substring(Settings.EnumPrefix.Length);
|
|
return ret;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ToString
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
List<Constant> constants = new List<Constant>(ConstantCollection.Values);
|
|
constants.Sort(delegate(Constant c1, Constant c2)
|
|
{
|
|
int ret = String.Compare(c1.Value, c2.Value);
|
|
if (ret == 0)
|
|
return String.Compare(c1.Name, c2.Name);
|
|
return ret;
|
|
});
|
|
|
|
if (IsFlagCollection)
|
|
sb.AppendLine("[Flags]");
|
|
sb.Append("public enum ");
|
|
sb.Append(Name);
|
|
sb.Append(" : ");
|
|
sb.AppendLine(Type);
|
|
sb.AppendLine("{");
|
|
|
|
foreach (Constant c in constants)
|
|
{
|
|
sb.Append(" ");
|
|
sb.Append(c.ToString());
|
|
if (!String.IsNullOrEmpty(c.ToString()))
|
|
sb.AppendLine(",");
|
|
}
|
|
sb.Append("}");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region class EnumCollection
|
|
|
|
public class EnumCollection : SortedDictionary<string, Enum>
|
|
{
|
|
internal void AddRange(EnumCollection enums)
|
|
{
|
|
foreach (Enum e in enums.Values)
|
|
Utilities.Merge(this, e);
|
|
}
|
|
|
|
internal void Translate(XPathDocument overrides)
|
|
{
|
|
if (overrides == null)
|
|
throw new ArgumentNullException("overrides");
|
|
|
|
string path = "/overrides/replace/enum[@name='{0}']";
|
|
|
|
// Translate enum names.
|
|
{
|
|
List<string> keys_to_update = new List<string>();
|
|
foreach (Enum e in Values)
|
|
{
|
|
string name = e.Name;
|
|
|
|
XPathNavigator enum_override = overrides.CreateNavigator().SelectSingleNode(String.Format(path, name));
|
|
if (enum_override != null)
|
|
{
|
|
XPathNavigator name_override = enum_override.SelectSingleNode("name");
|
|
if (name_override != null)
|
|
{
|
|
name = name_override.Value;
|
|
}
|
|
}
|
|
|
|
name = Enum.TranslateName(name);
|
|
if (name != e.Name)
|
|
{
|
|
keys_to_update.Add(e.Name);
|
|
e.Name = name;
|
|
}
|
|
}
|
|
|
|
foreach (string name in keys_to_update)
|
|
{
|
|
Enum e = this[name];
|
|
Remove(name);
|
|
Add(e.Name, e);
|
|
}
|
|
|
|
keys_to_update = null;
|
|
}
|
|
|
|
foreach (Enum e in Values)
|
|
{
|
|
XPathNavigator enum_override = overrides.CreateNavigator().SelectSingleNode(String.Format(path, e.Name));
|
|
foreach (Constant c in e.ConstantCollection.Values)
|
|
{
|
|
if (enum_override != null)
|
|
{
|
|
XPathNavigator constant_override = enum_override.SelectSingleNode(String.Format("token[@name='{0}']", c.PreviousName)) ??
|
|
enum_override.SelectSingleNode(String.Format("token[@name={0}]", c.Name));
|
|
if (constant_override != null)
|
|
{
|
|
foreach (XPathNavigator node in constant_override.SelectChildren(XPathNodeType.Element))
|
|
{
|
|
switch (node.Name)
|
|
{
|
|
case "name": c.Name = (string)node.TypedValue; break;
|
|
case "value": c.Value = (string)node.TypedValue; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// There are cases when a value is an aliased constant, with no enum specified.
|
|
// (e.g. FOG_COORD_ARRAY_TYPE = GL_FOG_COORDINATE_ARRAY_TYPE)
|
|
// In this case try searching all enums for the correct constant to alias (stupid opengl specs).
|
|
if (String.IsNullOrEmpty(c.Reference) && !Char.IsDigit(c.Value[0]))
|
|
{
|
|
foreach (Enum @enum in Values)
|
|
{
|
|
// Skip generic GLenum
|
|
if (@enum.Name == "GLenum")
|
|
continue;
|
|
|
|
if (@enum.ConstantCollection.ContainsKey(c.Value))
|
|
{
|
|
c.Reference = @enum.Name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (Enum e in Values)
|
|
{
|
|
restart:
|
|
foreach (Constant c in e.ConstantCollection.Values)
|
|
{
|
|
bool result = Constant.TranslateConstantWithReference(c, Enum.GLEnums, Enum.AuxEnums);
|
|
if (!result)
|
|
{
|
|
e.ConstantCollection.Remove(c.Name);
|
|
goto restart;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Settings.DropMultipleTokens)
|
|
{
|
|
// When there are multiple tokens with the same value but different extension
|
|
// drop the duplicates. Order of preference: core > ARB > EXT > vendor specific
|
|
|
|
List<Constant> removed_tokens = new List<Constant>();
|
|
|
|
foreach (Enum e in Values)
|
|
{
|
|
if (e.Name == "All")
|
|
continue;
|
|
|
|
// This implementation is a not very bright O(n^2).
|
|
foreach (Constant c in e.ConstantCollection.Values)
|
|
{
|
|
foreach (Constant c2 in e.ConstantCollection.Values)
|
|
{
|
|
if (c.Name != c2.Name && c.Value == c2.Value)
|
|
{
|
|
int prefer = OrderOfPreference(Utilities.GetGL2Extension(c.Name), Utilities.GetGL2Extension(c2.Name));
|
|
if (prefer == -1)
|
|
removed_tokens.Add(c2);
|
|
else if (prefer == 1)
|
|
removed_tokens.Add(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (Constant c in removed_tokens)
|
|
e.ConstantCollection.Remove(c.Name);
|
|
removed_tokens.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return -1 for ext1, 1 for ext2 or 0 if no preference.
|
|
int OrderOfPreference(string ext1, string ext2)
|
|
{
|
|
// If one is empty and the other not, prefer the empty one (empty == core)
|
|
// Otherwise check for Arb and Ext. To reuse the logic for the
|
|
// empty check, let's try to remove first Arb, then Ext from the strings.
|
|
int ret = PreferEmpty(ext1, ext2);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ext1 = ext1.Replace("Arb", ""); ext2 = ext2.Replace("Arb", "");
|
|
ret = PreferEmpty(ext1, ext2);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ext1 = ext1.Replace("Ext", ""); ext2 = ext2.Replace("Ext", "");
|
|
return PreferEmpty(ext1, ext2);
|
|
}
|
|
|
|
// Prefer the empty string over the non-empty.
|
|
int PreferEmpty(string ext1, string ext2)
|
|
{
|
|
if (String.IsNullOrEmpty(ext1) && !String.IsNullOrEmpty(ext2))
|
|
return -1;
|
|
else if (String.IsNullOrEmpty(ext2) && !String.IsNullOrEmpty(ext1))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
new bool TryGetValue(string key, out Enum value)
|
|
{
|
|
return base.TryGetValue(key, out value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|