public static bool Ends(this ITokenStream stream, string type, ILogger logger, out Token[] row, out string token0) { row = stream.PeekNonEmpty(); if (row == null) { token0 = null; return(true); } token0 = row[0].Value.ToLower(); if (token0 == "end") { if (row.Length > 1) { if (!string.Equals(row[1].Value, type, StringComparison.OrdinalIgnoreCase)) { logger?.WriteLine($"Warning: Expected 'end {type}' at {stream.Path}:{stream.Line}."); } } stream.Move(); return(true); } return(false); }
public static bool Starts(this ITokenStream stream, string type, ILogger logger, out string name) { var row = stream.PeekNonEmpty(); if (row == null) { name = null; return(false); } if (string.Equals(row[0].Value, type, StringComparison.OrdinalIgnoreCase)) { if (row.Length > 1) { name = row[1].Value; } else { name = null; logger?.WriteLine($"Warning: No item name at {stream.Path}:{stream.Line}. Items without a name may get ignored."); } stream.Move(); return(true); } name = null; return(false); }
public static HelpConfiguration ParseHelp(ITokenStream stream, ILogger logger, string suffix = "") { var key = "help" + suffix; if (!stream.Starts(key)) { return(null); } string text = null; var inputHelpTexts = new List <InputHelpText>(); while (true) { if (stream.Ends(key, logger, out var row, out var token0)) { break; } if (row.Length < 3 || row[1].Value != "=") { logger?.WriteLine($"Error: Malformed assignment at {stream.Path}:{stream.Line}."); stream.Move(); continue; } var rest = string.Join(" ", row.Skip(2)); if (token0 == "text") { text = rest; } else if (Enum.TryParse(token0, true, out HelpKey helpKey)) { inputHelpTexts.Add(new InputHelpText(helpKey, rest)); } else { logger?.WriteLine($"Warning: Invalid symbol '{token0}' at {stream.Path}:{stream.Line}."); } stream.Move(); } return(new HelpConfiguration(text, inputHelpTexts)); }
public static bool Starts(this ITokenStream stream, string type) { var row = stream.PeekNonEmpty(); if (row == null) { return(false); } if (string.Equals(row[0].Value, type, StringComparison.OrdinalIgnoreCase)) { stream.Move(); return(true); } return(false); }
public static Token[] PeekNonEmpty(this ITokenStream stream) { while (true) { var tokens = stream.Peek(); if (tokens == null) { return(null); } if (tokens.Length > 0) { return(tokens); } stream.Move(); } }
public static ProfileConfiguration ParseProfile(ITokenStream stream, ILogger logger) { if (!stream.Starts("profile", logger, out var name)) { return(null); } var bindings = new List <InputBinding>(); while (true) { if (stream.Ends("profile", logger, out var row, out var token0)) { break; } if (row.Length < 4 || row[1].Value != "=") { logger?.WriteLine($"Error: Malformed key binding at {stream.Path}:{stream.Line}."); stream.Move(); continue; } if (token0 == "ls" || token0 == "rs") { if (!string.Equals(row[2].Value, "map")) { logger?.WriteLine($"Error: Unknown analog mapping at {stream.Path}:{stream.Line}."); stream.Move(); continue; } var stick = token0 == "ls" ? ThumbStick.Left : ThumbStick.Right; switch (row[3].Value.ToLower()) { case "mouse": bindings.Add(new MouseMapping(stick)); break; case "scroll": bindings.Add(new ScrollMapping(stick)); break; case "radial_menu": bindings.Add(new RadialMenuMapping(stick)); break; default: logger?.WriteLine($"Error: Unknown analog mapping at {stream.Path}:{stream.Line}."); break; } stream.Move(); continue; } if (!Enum.TryParse(token0, true, out InputKey inputKey)) { logger?.WriteLine($"Error: Unknown key '{token0}' at {stream.Path}:{stream.Line}."); } int end; switch (row[2].Value.ToLower()) { case "map": if (string.Equals(row[3].Value, "mod", StringComparison.OrdinalIgnoreCase)) { bindings.Add(new ModMapping(inputKey)); break; } var keys = ParseKeyCodes(row, 3, out end); if (keys == null) { logger?.WriteLine($"Error: Invalid key code(s) at {stream.Path}:{stream.Line}."); } else { if (end < row.Length) { logger?.WriteLine($"Warning: Ignored unknown symbols at {stream.Path}:{stream.Line}."); } bindings.Add(new KeyMapping(inputKey, keys)); } break; case "press": var action = ParseAction(row, 3, out end); if (action == null) { logger?.WriteLine($"Error: Invalid action at {stream.Path}:{stream.Line}."); break; } if (end < row.Length) { var nextToken = row[end]; if (string.Equals(nextToken.Value, "repeat", StringComparison.OrdinalIgnoreCase)) { bindings.Add(new PressBinding(inputKey, action, true)); } else if (string.Equals(nextToken.Value, "hold", StringComparison.OrdinalIgnoreCase)) { end++; if (end >= row.Length) { logger?.WriteLine($"Error: Expected action after 'hold' at {stream.Path}:{stream.Line}."); break; } var holdAction = ParseAction(row, end, out end); if (holdAction == null) { logger?.WriteLine($"Error: Invalid action at {stream.Path}:{stream.Line}."); break; } if (end < row.Length) { logger?.WriteLine($"Warning: Ignored unknown symbols at {stream.Path}:{stream.Line}."); } bindings.Add(new PressHoldBinding(inputKey, action, holdAction)); } else { logger?.WriteLine($"Error: Invalid symbol '{nextToken}' at {stream.Path}:{stream.Line}."); } } else { bindings.Add(new PressBinding(inputKey, action, false)); } break; default: logger?.WriteLine($"Error: Unknown symbol '{row[2].Value}' at {stream.Path}:{stream.Line}."); break; } stream.Move(); } if (name == null) { return(null); } return(new ProfileConfiguration(name, bindings)); }
public static RootConfiguration Parse(ITokenStream stream, ILogger logger) { var config = new RootConfiguration(); while (true) { var tokens = stream.PeekNonEmpty(); if (tokens == null) { break; } var token0 = tokens[0].Value.ToLower(); var token1 = tokens.Length > 1 ? tokens[1].Value : null; var token2 = tokens.Length > 2 ? tokens[2].Value : null; switch (token0) { case "bind": var binding = ParseCommandBinding(stream, logger); if (binding != null) { config.Bindings.Add(binding); } stream.Move(); break; case "profile": var profile = ParseProfile(stream, logger); if (profile != null) { config.Profiles.Add(profile); } break; case "menu": var menu = ParseMenuConfiguration(stream, logger); if (menu != null) { config.Menus.Add(menu); } break; default: if (tokens.Length < 3 || token1 != "=") { logger?.WriteLine($"Error: Malformed line at {stream.Path}:{stream.Line}."); stream.Move(); break; } if (token0 == "fps") { if (InvariantDouble.TryParse(token2, out var fps) && fps >= 1d && fps <= 300d) { config.Fps = fps; } else { logger?.WriteLine( $"Warning: Invalid FPS value '{token2}' at '{token0}' at {stream.Path}:{stream.Line}."); } } else if (config.Descriptors.TryGetValue(token0, out var descriptor)) { if (!descriptor.isValidValue(token2)) { logger?.WriteLine( $"Warning: Value '{token2}' is invalid or out of range for key '{token0}' at {stream.Path}:{stream.Line}. Default value is assigned."); } descriptor.SetValue(token2); } else { logger?.WriteLine($"Error: Unknown key '{token0}' at {stream.Path}:{stream.Line}."); } stream.Move(); break; } } config.InitializeProfiles(); return(config); }
public static MenuItemConfiguration ParseMenuItem(ITokenStream stream, ILogger logger) { if (!stream.Starts("item")) { return(null); } HelpConfiguration help = null; HelpConfiguration help2 = null; var bindings = new List <CommandBinding>(); string name = null; string text = null; List <Token> icon = null; while (true) { if (stream.Ends("item", logger, out var row, out var token0)) { break; } if (token0 == "help") { var h = ParseHelp(stream, logger); if (h != null) { help = h; } continue; } if (token0 == "help2") { var h2 = ParseHelp(stream, logger, "2"); if (h2 != null) { h2.Orientation = Orientation.Horizontal; help2 = h2; } continue; } if (token0 == "bind") { var binding = ParseCommandBinding(stream, logger); if (binding != null) { bindings.Add(binding); } stream.Move(); continue; } if (row.Length < 3 || row[1].Value != "=") { logger?.WriteLine($"Error: Malformed assignment at {stream.Path}:{stream.Line}."); stream.Move(); continue; } var rest = string.Join(" ", row.Skip(2)); switch (token0) { case "text": text = rest; break; case "icon": icon = row.Skip(2).ToList(); break; case "name": name = rest; break; default: logger?.UnexpectedLine(stream); break; } stream.Move(); } return(new MenuItemConfiguration(name, icon, text, bindings, help, help2)); }
public static PageConfiguration ParsePage(ITokenStream stream, ILogger logger) { if (!stream.Starts("page")) { return(null); } HelpConfiguration help = null; HelpConfiguration help2 = null; var bindings = new List <CommandBinding>(); var items = new List <MenuItemConfiguration>(); while (true) { if (stream.Ends("page", logger, out var row, out var token0)) { break; } switch (token0) { case "help": var h = ParseHelp(stream, logger); if (h != null) { help = h; } continue; case "bind": var binding = ParseCommandBinding(stream, logger); if (binding != null) { bindings.Add(binding); } stream.Move(); continue; case "help2": var h2 = ParseHelp(stream, logger, "2"); if (h2 != null) { h2.Orientation = Orientation.Horizontal; help2 = h2; } continue; case "item": var item = ParseMenuItem(stream, logger); if (item != null) { items.Add(item); } continue; default: logger?.UnexpectedLine(stream); stream.Move(); continue; } } return(new PageConfiguration(help, help2, bindings, items)); }
public static MenuConfiguration ParseMenuConfiguration(ITokenStream stream, ILogger logger) { if (!stream.Starts("menu", logger, out var name)) { return(null); } HelpConfiguration help = null; HelpConfiguration help2 = null; var bindings = new List <CommandBinding>(); var pages = new List <PageConfiguration>(); while (true) { if (stream.Ends("menu", logger, out var row, out var token0)) { break; } switch (token0) { case "bind": var binding = ParseCommandBinding(stream, logger); if (binding != null) { bindings.Add(binding); } stream.Move(); continue; case "help": var h = ParseHelp(stream, logger); if (h != null) { help = h; } continue; case "help2": var h2 = ParseHelp(stream, logger, "2"); if (h2 != null) { h2.Orientation = Orientation.Horizontal; help2 = h2; } continue; case "page": var page = ParsePage(stream, logger); if (page != null) { pages.Add(page); } continue; default: logger?.UnexpectedLine(stream); stream.Move(); continue; } } return(name != null ? new MenuConfiguration(name, help, help2, bindings, pages) : null); }