From fd1590142ece2f1d3951f9dffbe8867bc5d948ca Mon Sep 17 00:00:00 2001 From: Frank Skare Date: Sun, 27 Jun 2021 13:11:34 +0200 Subject: [PATCH] toml parser replaced with own conf parser --- docs/Manual.md | 17 +- src/DynamicGUI/Tommy.cs | 1824 ----------------- src/Misc/Common.cs | 69 + .../DynamicGUI.cs => Misc/Conf.cs} | 62 +- src/Properties/Resources.Designer.cs | 26 +- src/Properties/Resources.resx | 4 +- src/Resources/editor.toml.txt | 618 ------ src/Resources/editor_conf.txt | 656 ++++++ src/WPF/ConfWindow.xaml.cs | 2 +- .../OptionSettingControl.xaml | 2 +- .../OptionSettingControl.xaml.cs | 0 .../StringSettingControl.xaml | 2 +- .../StringSettingControl.xaml.cs | 0 src/mpv.net.csproj | 14 +- 14 files changed, 790 insertions(+), 2506 deletions(-) delete mode 100644 src/DynamicGUI/Tommy.cs create mode 100644 src/Misc/Common.cs rename src/{DynamicGUI/DynamicGUI.cs => Misc/Conf.cs} (69%) delete mode 100644 src/Resources/editor.toml.txt create mode 100644 src/Resources/editor_conf.txt rename src/{DynamicGUI => WPF}/OptionSettingControl.xaml (98%) rename src/{DynamicGUI => WPF}/OptionSettingControl.xaml.cs (100%) rename src/{DynamicGUI => WPF}/StringSettingControl.xaml (98%) rename src/{DynamicGUI => WPF}/StringSettingControl.xaml.cs (100%) diff --git a/docs/Manual.md b/docs/Manual.md index 7e23a60..9b775ee 100644 --- a/docs/Manual.md +++ b/docs/Manual.md @@ -374,9 +374,9 @@ Alternatively the Chrome/Firefox extension [Open With](../../../issues/119) can [Open with++](https://github.com/stax76/OpenWithPlusPlus) can be used to extend the File Explorer context menu to get menu items for [Play with mpv.net](https://github.com/stax76/OpenWithPlusPlus#play-with-mpvnet) and [Add to mpv.net playlist](https://github.com/stax76/OpenWithPlusPlus#add-to-mpvnet-playlist). -### Universal Remote +### Universal Remote Android app -Universal Remote is a non-free Android remote control app. +Universal Remote is Android remote control app which costs 5 €. https://www.unifiedremote.com @@ -390,6 +390,14 @@ https://www.unifiedremote.com/tutorials/how-to-install-a-custom-remote [My config](./Universal%20Remote) +Very useful is the Universal Remote File Browser feature. + + +### One For All Contour URC1210 and FLIRC USB + +My primary remote control solution however is a One For All Contour URC1210 +using Philips code 0556 together with FLIRC USB (gen2). + Scripting --------- @@ -633,15 +641,10 @@ The Extension implementation is based on the The main window is WinForms based because WinForms allows better libmpv integration compared to WPF, all other windows are WPF based. -The config editor adds it's controls dynamically and uses -[TOML](https://en.wikipedia.org/wiki/TOML) to define it's content. - - Third party components are: - [libmpv provides the core functionality](https://mpv.io/) - [MediaInfo](https://mediaarea.net/en/MediaInfo) -- [Tommy, a single file TOML parser](https://github.com/dezhidki/Tommy) - [Everything, a fast file search service](https://www.voidtools.com) diff --git a/src/DynamicGUI/Tommy.cs b/src/DynamicGUI/Tommy.cs deleted file mode 100644 index d0e90d0..0000000 --- a/src/DynamicGUI/Tommy.cs +++ /dev/null @@ -1,1824 +0,0 @@ -#region LICENSE -/** - * MIT License - * - * Copyright (c) 2019 Denis Zhidkikh - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#endregion -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -namespace Tommy -{ - #region TOML Nodes - - public abstract class TomlNode : IEnumerable - { - public virtual bool HasValue { get; } = false; - public virtual bool IsArray { get; } = false; - public virtual bool IsTable { get; } = false; - public virtual bool IsString { get; } = false; - public virtual bool IsInteger { get; } = false; - public virtual bool IsFloat { get; } = false; - public virtual bool IsDateTime { get; } = false; - public virtual bool IsBoolean { get; } = false; - public virtual string Comment { get; set; } - - public virtual TomlTable AsTable => this as TomlTable; - public virtual TomlString AsString => this as TomlString; - public virtual TomlInteger AsInteger => this as TomlInteger; - public virtual TomlFloat AsFloat => this as TomlFloat; - public virtual TomlBoolean AsBoolean => this as TomlBoolean; - public virtual TomlDateTime AsDateTime => this as TomlDateTime; - public virtual TomlArray AsArray => this as TomlArray; - - public virtual int ChildrenCount => 0; - - public virtual TomlNode this[string key] { get => null; set { } } - - public virtual TomlNode this[int index] { get => null; set { } } - - public virtual IEnumerable Children { get { yield break; } } - - public virtual IEnumerable Keys { get { yield break; } } - - public IEnumerator GetEnumerator() => Children.GetEnumerator(); - - public virtual bool TryGetNode(string key, out TomlNode node) - { - node = null; - return false; - } - - public virtual bool HasKey(string key) => false; - - public virtual bool HasItemAt(int index) => false; - - public virtual void Add(string key, TomlNode node) { } - - public virtual void Add(TomlNode node) { } - - public virtual void Delete(TomlNode node) { } - - public virtual void Delete(string key) { } - - public virtual void Delete(int index) { } - - public virtual void AddRange(IEnumerable nodes) - { - foreach (var tomlNode in nodes) Add(tomlNode); - } - - public virtual void ToTomlString(TextWriter tw, string name = null) { } - - #region Native type to TOML cast - - public static implicit operator TomlNode(string value) => - new TomlString - { - Value = value - }; - - public static implicit operator TomlNode(bool value) => - new TomlBoolean - { - Value = value - }; - - public static implicit operator TomlNode(long value) => - new TomlInteger - { - Value = value - }; - - public static implicit operator TomlNode(float value) => - new TomlFloat - { - Value = value - }; - - public static implicit operator TomlNode(double value) => - new TomlFloat - { - Value = value - }; - - public static implicit operator TomlNode(DateTime value) => - new TomlDateTime - { - Value = value - }; - - public static implicit operator TomlNode(TomlNode[] nodes) - { - var result = new TomlArray(); - result.AddRange(nodes); - return result; - } - - #endregion - - #region TOML to native type cast - - public static implicit operator string(TomlNode value) => value.ToString(); - - public static implicit operator int(TomlNode value) => (int)value.AsInteger.Value; - - public static implicit operator long(TomlNode value) => value.AsInteger.Value; - - public static implicit operator float(TomlNode value) => (float)value.AsFloat.Value; - - public static implicit operator double(TomlNode value) => value.AsFloat.Value; - - public static implicit operator bool(TomlNode value) => value.AsBoolean.Value; - - public static implicit operator DateTime(TomlNode value) => value.AsDateTime.Value; - - #endregion - } - - public class TomlString : TomlNode - { - public override bool HasValue { get; } = true; - public override bool IsString { get; } = true; - public bool IsMultiline { get; set; } - public bool PreferLiteral { get; set; } - - public string Value { get; set; } - - public override string ToString() => Value; - - public override void ToTomlString(TextWriter tw, string name = null) - { - if (Value.IndexOf(TomlSyntax.LITERAL_STRING_SYMBOL) != -1 && PreferLiteral) PreferLiteral = false; - - var quotes = new string(PreferLiteral ? TomlSyntax.LITERAL_STRING_SYMBOL : TomlSyntax.BASIC_STRING_SYMBOL, - IsMultiline ? 3 : 1); - var result = PreferLiteral ? Value : Value.Escape(!IsMultiline); - tw.Write(quotes); - tw.Write(result); - tw.Write(quotes); - } - } - - public class TomlInteger : TomlNode - { - public enum Base - { - Binary = 2, - Octal = 8, - Decimal = 10, - Hexadecimal = 16 - } - - public override bool IsInteger { get; } = true; - public override bool HasValue { get; } = true; - public Base IntegerBase { get; set; } = Base.Decimal; - - public long Value { get; set; } - - public override string ToString() - { - if (IntegerBase != Base.Decimal) - return $"0{TomlSyntax.BaseIdentifiers[(int)IntegerBase]}{Convert.ToString(Value, (int)IntegerBase)}"; - return Value.ToString(CultureInfo.InvariantCulture); - } - - public override void ToTomlString(TextWriter tw, string name = null) => tw.Write(ToString()); - } - - public class TomlFloat : TomlNode - { - public override bool IsFloat { get; } = true; - public override bool HasValue { get; } = true; - - public double Value { get; set; } - - public override string ToString() - { - if (double.IsNaN(Value)) return TomlSyntax.NAN_VALUE; - - if (double.IsPositiveInfinity(Value)) return TomlSyntax.INF_VALUE; - - if (double.IsNegativeInfinity(Value)) return TomlSyntax.NEG_INF_VALUE; - - return Value.ToString("G", CultureInfo.InvariantCulture); - } - - public override void ToTomlString(TextWriter tw, string name = null) => tw.Write(ToString()); - } - - public class TomlBoolean : TomlNode - { - public override bool IsBoolean { get; } = true; - public override bool HasValue { get; } = true; - - public bool Value { get; set; } - - public override string ToString() => Value ? TomlSyntax.TRUE_VALUE : TomlSyntax.FALSE_VALUE; - - public override void ToTomlString(TextWriter tw, string name = null) => tw.Write(ToString()); - } - - public class TomlDateTime : TomlNode - { - public override bool IsDateTime { get; } = true; - public override bool HasValue { get; } = true; - public bool OnlyDate { get; set; } - public bool OnlyTime { get; set; } - public int SecondsPrecision { get; set; } - - public DateTime Value { get; set; } - - public override string ToString() - { - if (OnlyDate) return Value.ToString(TomlSyntax.LocalDateFormat); - if (OnlyTime) return Value.ToString(TomlSyntax.RFC3339LocalTimeFormats[SecondsPrecision]); - if (Value.Kind == DateTimeKind.Local) - return Value.ToString(TomlSyntax.RFC3339LocalDateTimeFormats[SecondsPrecision]); - return Value.ToString(TomlSyntax.RFC3339Formats[SecondsPrecision]); - } - - public override void ToTomlString(TextWriter tw, string name = null) => tw.Write(ToString()); - } - - public class TomlArray : TomlNode - { - List values; - - public override bool HasValue { get; } = true; - public override bool IsArray { get; } = true; - public bool IsTableArray { get; set; } - public List RawArray => values ?? (values = new List()); - - public override TomlNode this[int index] { - get { - if (index < RawArray.Count) return RawArray[index]; - var lazy = new TomlLazy(this); - this[index] = lazy; - return lazy; - } - set { - if (index == RawArray.Count) - RawArray.Add(value); - else - RawArray[index] = value; - } - } - - public override int ChildrenCount => RawArray.Count; - - public override IEnumerable Children => RawArray.AsEnumerable(); - - public override void Add(TomlNode node) => RawArray.Add(node); - - public override void AddRange(IEnumerable nodes) => RawArray.AddRange(nodes); - - public override void Delete(TomlNode node) => RawArray.Remove(node); - - public override void Delete(int index) => RawArray.RemoveAt(index); - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(TomlSyntax.ARRAY_START_SYMBOL); - - if (ChildrenCount != 0) - { - sb.Append(' '); - foreach (var tomlNode in RawArray) - sb.Append(tomlNode.ToString()).Append(TomlSyntax.ITEM_SEPARATOR).Append(' '); - } - - sb.Append(TomlSyntax.ARRAY_END_SYMBOL); - return sb.ToString(); - } - - public override void ToTomlString(TextWriter tw, string name = null) - { - // If it's a normal array, write it as usual - if (!IsTableArray) - { - tw.Write(ToString()); - return; - } - - tw.WriteLine(); - - Comment?.AsComment(tw); - tw.Write(TomlSyntax.ARRAY_START_SYMBOL); - tw.Write(TomlSyntax.ARRAY_START_SYMBOL); - tw.Write(name); - tw.Write(TomlSyntax.ARRAY_END_SYMBOL); - tw.Write(TomlSyntax.ARRAY_END_SYMBOL); - tw.WriteLine(); - - var first = true; - - foreach (var tomlNode in RawArray) - { - if (!(tomlNode is TomlTable tbl)) - throw new TomlFormatException("The array is marked as array table but contains non-table nodes!"); - - // Ensure it's parsed as a section - tbl.IsInline = false; - - if (!first) - { - tw.WriteLine(); - - Comment?.AsComment(tw); - tw.Write(TomlSyntax.ARRAY_START_SYMBOL); - tw.Write(TomlSyntax.ARRAY_START_SYMBOL); - tw.Write(name); - tw.Write(TomlSyntax.ARRAY_END_SYMBOL); - tw.Write(TomlSyntax.ARRAY_END_SYMBOL); - tw.WriteLine(); - } - - first = false; - - // Don't pass section name because we already specified it - tbl.ToTomlString(tw); - - tw.WriteLine(); - } - } - } - - public class TomlTable : TomlNode - { - Dictionary children; - - public override bool HasValue { get; } = false; - public override bool IsTable { get; } = true; - public bool IsInline { get; set; } - public Dictionary RawTable => children ?? (children = new Dictionary()); - - public override TomlNode this[string key] { - get { - if (RawTable.TryGetValue(key, out var result)) - return result; - - var lazy = new TomlLazy(this); - RawTable[key] = lazy; - return lazy; - } - set => RawTable[key] = value; - } - - public override int ChildrenCount => RawTable.Count; - - public override IEnumerable Children => RawTable.Select(kv => kv.Value); - - public override IEnumerable Keys => RawTable.Select(kv => kv.Key); - - public override bool HasKey(string key) => RawTable.ContainsKey(key); - - public override void Add(string key, TomlNode node) => RawTable.Add(key, node); - - public override bool TryGetNode(string key, out TomlNode node) => RawTable.TryGetValue(key, out node); - - public override void Delete(TomlNode node) => RawTable.Remove(RawTable.First(kv => kv.Value == node).Key); - - public override void Delete(string key) => RawTable.Remove(key); - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(TomlSyntax.INLINE_TABLE_START_SYMBOL); - - if (ChildrenCount != 0) - { - sb.Append(' '); - foreach (var child in RawTable) - sb.Append(child.Key) - .Append(' ') - .Append(TomlSyntax.KEY_VALUE_SEPARATOR) - .Append(' ') - .Append(child.Value.ToString()) - .Append(TomlSyntax.ITEM_SEPARATOR) - .Append(' '); - } - - sb.Append(TomlSyntax.INLINE_TABLE_END_SYMBOL); - return sb.ToString(); - } - - public override void ToTomlString(TextWriter tw, string name = null) - { - // The table is inline table - if (IsInline && name != null) - { - tw.Write(ToString()); - return; - } - - Comment?.AsComment(tw); - - if (name != null) - { - tw.Write(TomlSyntax.ARRAY_START_SYMBOL); - tw.Write(name); - tw.Write(TomlSyntax.ARRAY_END_SYMBOL); - tw.WriteLine(); - } - else if (Comment != null) // Add some spacing between the first node and the comment - tw.WriteLine(); - - var namePrefix = name == null ? "" : $"{name}."; - var first = true; - - var sectionableItems = new Dictionary(); - - foreach (var child in RawTable) - { - // If value should be parsed as section, separate if from the bunch - if (child.Value is TomlArray arr && arr.IsTableArray || child.Value is TomlTable tbl && !tbl.IsInline) - { - sectionableItems.Add(child.Key, child.Value); - continue; - } - - if (!first) tw.WriteLine(); - first = false; - - var key = child.Key.AsKey(); - child.Value.Comment?.AsComment(tw); - tw.Write(key); - tw.Write(' '); - tw.Write(TomlSyntax.KEY_VALUE_SEPARATOR); - tw.Write(' '); - - child.Value.ToTomlString(tw, $"{namePrefix}{key}"); - } - - if (sectionableItems.Count == 0) return; - - tw.WriteLine(); - tw.WriteLine(); - first = true; - foreach (var child in sectionableItems) - { - if (!first) tw.WriteLine(); - first = false; - - child.Value.ToTomlString(tw, $"{namePrefix}{child.Key}"); - } - } - } - - class TomlLazy : TomlNode - { - readonly TomlNode parent; - TomlNode replacement; - - public TomlLazy(TomlNode parent) => this.parent = parent; - - public override TomlNode this[int index] { - get => Set()[index]; - set => Set()[index] = value; - } - - public override TomlNode this[string key] { - get => Set()[key]; - set => Set()[key] = value; - } - - public override void Add(TomlNode node) => Set().Add(node); - - public override void Add(string key, TomlNode node) => Set().Add(key, node); - - public override void AddRange(IEnumerable nodes) => Set().AddRange(nodes); - - TomlNode Set() where T : TomlNode, new() - { - if (replacement != null) return replacement; - - var newNode = new T - { - Comment = Comment - }; - - if (parent.IsTable) - { - var key = parent.Keys.FirstOrDefault(s => parent.TryGetNode(s, out var node) && node.Equals(this)); - if (key == null) return default(T); - - parent[key] = newNode; - } - else if (parent.IsArray) - { - var index = 0; - foreach (var child in parent.Children) - { - if (child == this) break; - index++; - } - - if (index == parent.ChildrenCount) return default(T); - - parent[index] = newNode; - } - else - return default(T); - - replacement = newNode; - return newNode; - } - } - - #endregion - - public static class TOML - { - public enum ParseState - { - None, - KeyValuePair, - SkipToNextLine, - Table - } - - public static bool ForceASCII { get; set; } = false; - - public static TomlTable Parse(TextReader reader) - { - var rootNode = new TomlTable(); - var currentNode = rootNode; - var state = ParseState.None; - var keyParts = new List(); - var arrayTable = false; - var latestComment = new StringBuilder(); - var firstComment = true; - - int currentChar; - while ((currentChar = reader.Peek()) >= 0) - { - var c = (char)currentChar; - - if (state == ParseState.None) - { - // Skip white space - if (TomlSyntax.IsWhiteSpace(c)) goto consume_character; - - if (TomlSyntax.IsNewLine(c)) - { - // Check if there are any comments and so far no items being declared - if (latestComment.Length != 0 && firstComment) - { - rootNode.Comment = latestComment.ToString().TrimEnd(); - latestComment.Length = 0; - firstComment = false; - } - - goto consume_character; - } - - // Start of a comment; ignore until newline - if (c == TomlSyntax.COMMENT_SYMBOL) - { - // Consume the comment symbol and buffer the whole comment line - reader.Read(); - latestComment.AppendLine(reader.ReadLine()?.Trim()); - continue; - } - - // Encountered a non-comment value. The comment must belong to it (ignore possible newlines)! - firstComment = false; - - if (c == TomlSyntax.TABLE_START_SYMBOL) - { - state = ParseState.Table; - goto consume_character; - } - - if (TomlSyntax.IsBareKey(c) || TomlSyntax.IsQuoted(c)) - state = ParseState.KeyValuePair; - else - throw new TomlParseException($"Unexpected character \"{c}\"", state); - } - - if (state == ParseState.KeyValuePair) - { - var keyValuePair = ReadKeyValuePair(reader, keyParts); - keyValuePair.Comment = latestComment.ToString().TrimEnd(); - InsertNode(keyValuePair, currentNode, keyParts); - latestComment.Length = 0; - keyParts.Clear(); - state = ParseState.SkipToNextLine; - continue; - } - - if (state == ParseState.Table) - { - if (keyParts.Count == 0) - { - // We have array table - if (c == TomlSyntax.TABLE_START_SYMBOL) - { - // Consume the character - reader.Read(); - arrayTable = true; - } - - ReadKeyName(reader, ref keyParts, TomlSyntax.TABLE_END_SYMBOL, true); - if (keyParts.Count == 0) throw new TomlParseException("Table name is emtpy.", state); - - continue; - } - - if (c == TomlSyntax.TABLE_END_SYMBOL) - { - if (arrayTable) - { - if (reader.Peek() < 0 || (char)reader.Peek() != TomlSyntax.TABLE_END_SYMBOL) - throw new - TomlParseException($"Array table {".".Join(keyParts)} has only one closing bracket.", - state); - // Consume the extra closing table symbol - reader.Read(); - } - - currentNode = CreateTable(rootNode, keyParts, arrayTable); - currentNode.IsInline = false; - currentNode.Comment = latestComment.ToString().TrimEnd(); - keyParts.Clear(); - arrayTable = false; - latestComment.Length = 0; - state = ParseState.SkipToNextLine; - goto consume_character; - } - - if (keyParts.Count != 0) throw new TomlParseException($"Unexpected character \"{c}\"", state); - } - - if (state == ParseState.SkipToNextLine) - { - if (TomlSyntax.IsWhiteSpace(c) || c == TomlSyntax.NEWLINE_CARRIAGE_RETURN_CHARACTER) - goto consume_character; - - if (c == TomlSyntax.COMMENT_SYMBOL || c == TomlSyntax.NEWLINE_CHARACTER) - { - state = ParseState.None; - if (c == TomlSyntax.COMMENT_SYMBOL) - { - reader.ReadLine(); - continue; - } - - goto consume_character; - } - - throw new TomlParseException($"Unexpected character \"{c}\" at the end of the line.", state); - } - - consume_character: - reader.Read(); - } - - if (state != ParseState.None && state != ParseState.SkipToNextLine) - throw new TomlParseException("Unexpected end of file!", state); - - return rootNode; - } - - #region Key-Value pair parsing - - /** - * Reads a single key-value pair. - * Assumes the cursor is at the first character that belong to the pair (including possible whitespace). - * Consumes all characters that belong to the key and the value (ignoring possible trailing whitespace at the end). - * - * Example: - * foo = "bar" ==> foo = "bar" - * ^ ^ - */ - static TomlNode ReadKeyValuePair(TextReader reader, List keyParts) - { - int cur; - while ((cur = reader.Peek()) >= 0) - { - var c = (char)cur; - - if (TomlSyntax.IsQuoted(c) || TomlSyntax.IsBareKey(c)) - { - if (keyParts.Count != 0) - throw new TomlParseException("Encountered extra characters in key definition!", - ParseState.KeyValuePair); - - ReadKeyName(reader, ref keyParts, TomlSyntax.KEY_VALUE_SEPARATOR); - continue; - } - - if (TomlSyntax.IsWhiteSpace(c)) - { - reader.Read(); - continue; - } - - if (c == TomlSyntax.KEY_VALUE_SEPARATOR) - { - reader.Read(); - return ReadValue(reader); - } - - throw new TomlParseException($"Unexpected character \"{c}\" in key name.", ParseState.KeyValuePair); - } - - return null; - } - - /** - * Reads a single value. - * Assumes the cursor is at the first character that belongs to the value (including possible starting whitespace). - * Consumes all characters belonging to the value (ignoring possible trailing whitespace at the end). - * - * Example: - * "test" ==> "test" - * ^ ^ - */ - static TomlNode ReadValue(TextReader reader, bool skipNewlines = false) - { - int cur; - while ((cur = reader.Peek()) >= 0) - { - var c = (char)cur; - - if (TomlSyntax.IsWhiteSpace(c)) - { - reader.Read(); - continue; - } - - if (c == TomlSyntax.COMMENT_SYMBOL) - throw new TomlParseException("No value found!", ParseState.KeyValuePair); - - if (TomlSyntax.IsNewLine(c)) - { - if (skipNewlines) - { - reader.Read(); - continue; - } - - throw new TomlParseException("Encountered a newline when expecting a value!", - ParseState.KeyValuePair); - } - - if (TomlSyntax.IsQuoted(c)) - { - var isMultiline = IsTripleQuote(c, reader, out var excess); - var value = isMultiline - ? ReadQuotedValueMultiLine(c, reader) - : ReadQuotedValueSingleLine(c, reader, excess); - - return new TomlString - { - Value = value, - IsMultiline = isMultiline, - PreferLiteral = c == TomlSyntax.LITERAL_STRING_SYMBOL - }; - } - - if (c == TomlSyntax.INLINE_TABLE_START_SYMBOL) return ReadInlineTable(reader); - - if (c == TomlSyntax.ARRAY_START_SYMBOL) return ReadArray(reader); - - return ReadTomlValue(reader); - } - - return null; - } - - /** - * Reads a single key name. - * Assumes the cursor is at the first character belonging to the key (with possible trailing whitespace if `skipWhitespace = true`). - * Consumes all the characters until the `until` character is met (but does not consume the character itself). - * - * Example 1: - * foo.bar ==> foo.bar (`skipWhitespace = false`, `until = ' '`) - * ^ ^ - * - * Example 2: - * [ foo . bar ] ==> [ foo . bar ] (`skipWhitespace = true`, `until = ']'`) - * ^ ^ - */ - static void ReadKeyName(TextReader reader, ref List parts, char until, bool skipWhitespace = false) - { - var buffer = new StringBuilder(); - var quoted = false; - var prevWasSpace = false; - int cur; - while ((cur = reader.Peek()) >= 0) - { - var c = (char)cur; - - // Reached the final character - if (c == until) break; - - if (TomlSyntax.IsWhiteSpace(c)) - if (skipWhitespace) - { - prevWasSpace = true; - goto consume_character; - } - else - break; - - if (buffer.Length == 0) prevWasSpace = false; - - if (c == TomlSyntax.SUBKEY_SEPARATOR) - { - if (buffer.Length == 0) - throw new TomlParseException($"Found an extra subkey separator in {".".Join(parts)}...", - ParseState.KeyValuePair); - - parts.Add(buffer.ToString()); - buffer.Length = 0; - quoted = false; - prevWasSpace = false; - goto consume_character; - } - - if (prevWasSpace) throw new TomlParseException("Invalid spacing in key name", ParseState.KeyValuePair); - - if (TomlSyntax.IsQuoted(c)) - { - if (quoted) - throw new TomlParseException("Expected a subkey separator but got extra data instead!", - ParseState.KeyValuePair); - if (buffer.Length != 0) - throw new TomlParseException("Encountered a quote in the middle of subkey name!", - ParseState.KeyValuePair); - - // Consume the quote character and read the key name - buffer.Append(ReadQuotedValueSingleLine((char)reader.Read(), reader)); - quoted = true; - continue; - } - - if (TomlSyntax.IsBareKey(c)) - { - buffer.Append(c); - goto consume_character; - } - - // If we see an invalid symbol, let the next parser handle it - break; - - consume_character: - reader.Read(); - } - - if (buffer.Length == 0) - throw new TomlParseException($"Found an extra subkey separator in {".".Join(parts)}...", - ParseState.KeyValuePair); - - parts.Add(buffer.ToString()); - } - - #endregion - - #region Non-string value parsing - - /** - * Reads the whole raw value until the first non-value character is encountered. - * Assumes the cursor start position at the first value character and consumes all characters that may be related to the value. - * Example: - * - * 1_0_0_0 ==> 1_0_0_0 - * ^ ^ - */ - static string ReadRawValue(TextReader reader) - { - var result = new StringBuilder(); - - int cur; - while ((cur = reader.Peek()) >= 0) - { - var c = (char)cur; - - if (c == TomlSyntax.COMMENT_SYMBOL || TomlSyntax.IsNewLine(c) || TomlSyntax.IsValueSeparator(c)) break; - - result.Append(c); - - reader.Read(); - } - - // Replace trim with manual space counting? - return result.ToString().Trim(); - } - - /** - * Reads and parses a non-string, non-composite TOML value. - * Assumes the cursor at the first character that is related to the value (with possible spaces). - * Consumes all the characters that are related to the value. - * - * Example - * 1_0_0_0 # This is a comment ==> 1_0_0_0 # This is a comment - * ^ ^ - */ - static TomlNode ReadTomlValue(TextReader reader) - { - var value = ReadRawValue(reader); - - if (TomlSyntax.IsBoolean(value)) return bool.Parse(value); - - if (TomlSyntax.IsNaN(value)) return double.NaN; - - if (TomlSyntax.IsPosInf(value)) return double.PositiveInfinity; - - if (TomlSyntax.IsNegInf(value)) return double.NegativeInfinity; - - if (TomlSyntax.IsInteger(value)) - return long.Parse(value.RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR), CultureInfo.InvariantCulture); - - if (TomlSyntax.IsFloat(value)) - return double.Parse(value.RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR), CultureInfo.InvariantCulture); - - if (TomlSyntax.IsIntegerWithBase(value, out var numberBase)) - return new TomlInteger - { - Value = Convert.ToInt64(value.Substring(2).RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR), numberBase), - IntegerBase = (TomlInteger.Base)numberBase - }; - - value = value.Replace("T", " "); - if (StringUtils.TryParseDateTime(value, - TomlSyntax.RFC3339LocalDateTimeFormats, - DateTimeStyles.AssumeLocal, - out var dateTimeResult, - out var precision)) - return new TomlDateTime - { - Value = dateTimeResult, - SecondsPrecision = precision - }; - - if (StringUtils.TryParseDateTime(value, - TomlSyntax.RFC3339Formats, - DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, - out dateTimeResult, - out precision)) - return new TomlDateTime - { - Value = dateTimeResult, - SecondsPrecision = precision - }; - - - if (DateTime.TryParseExact(value, - TomlSyntax.LocalDateFormat, - CultureInfo.InvariantCulture, - DateTimeStyles.AssumeLocal, - out dateTimeResult)) - return new TomlDateTime - { - Value = dateTimeResult, - OnlyDate = true - }; - - if (StringUtils.TryParseDateTime(value, - TomlSyntax.RFC3339LocalTimeFormats, - DateTimeStyles.AssumeLocal, - out dateTimeResult, - out precision)) - return new TomlDateTime - { - Value = dateTimeResult, - OnlyTime = true, - SecondsPrecision = precision - }; - - throw new TomlParseException($"Value \"{value}\" is not a valid TOML 0.5.0 value!", - ParseState.KeyValuePair); - } - - /** - * Reads an array value. - * Assumes the cursor is at the start of the array definition. Reads all character until the array closing bracket. - * - * Example: - * [1, 2, 3] ==> [1, 2, 3] - * ^ ^ - */ - static TomlArray ReadArray(TextReader reader) - { - // Consume the start of array character - reader.Read(); - - var result = new TomlArray(); - - TomlNode currentValue = null; - - int cur; - while ((cur = reader.Peek()) >= 0) - { - var c = (char)cur; - - if (c == TomlSyntax.ARRAY_END_SYMBOL) - { - reader.Read(); - break; - } - - if (c == TomlSyntax.COMMENT_SYMBOL) - { - reader.ReadLine(); - continue; - } - - if (TomlSyntax.IsWhiteSpace(c) || TomlSyntax.IsNewLine(c)) goto consume_character; - - if (c == TomlSyntax.ITEM_SEPARATOR) - { - if (currentValue == null) - throw new TomlParseException("Encountered multiple value separators in an array!", - ParseState.KeyValuePair); - - result.Add(currentValue); - currentValue = null; - goto consume_character; - } - - currentValue = ReadValue(reader, true); - - if (result.ChildrenCount != 0 && result[0].GetType() != currentValue.GetType()) - throw new TomlParseException("Arrays cannot have mixed types!", ParseState.KeyValuePair); - - continue; - - consume_character: - reader.Read(); - } - - if (currentValue != null) result.Add(currentValue); - - return result; - } - - /** - * Reads an inline table. - * Assumes the cursor is at the start of the table definition. Reads all character until the table closing bracket. - * - * Example: - * { test = "foo", value = 1 } ==> { test = "foo", value = 1 } - * ^ ^ - */ - static TomlNode ReadInlineTable(TextReader reader) - { - reader.Read(); - - var result = new TomlTable - { - IsInline = true - }; - - TomlNode currentValue = null; - - var keyParts = new List(); - - int cur; - while ((cur = reader.Peek()) >= 0) - { - var c = (char)cur; - - if (c == TomlSyntax.INLINE_TABLE_END_SYMBOL) - { - reader.Read(); - break; - } - - if (c == TomlSyntax.COMMENT_SYMBOL) - throw new TomlParseException("Incomplete inline table definition!", ParseState.Table); - - if (TomlSyntax.IsNewLine(c)) - throw new TomlParseException("Inline tables are only allowed to be on single line", - ParseState.Table); - - if (TomlSyntax.IsWhiteSpace(c)) goto consume_character; - - if (c == TomlSyntax.ITEM_SEPARATOR) - { - if (currentValue == null) - throw new TomlParseException("Encountered multiple value separators in inline table!", - ParseState.Table); - - InsertNode(currentValue, result, keyParts); - keyParts.Clear(); - currentValue = null; - goto consume_character; - } - - currentValue = ReadKeyValuePair(reader, keyParts); - continue; - - consume_character: - reader.Read(); - } - - if (currentValue != null) InsertNode(currentValue, result, keyParts); - - return result; - } - - #endregion - - #region String parsing - - /** - * Checks if the string value a multiline string (i.e. a triple quoted string). - * Assumes the cursor is at the first quote character. Consumes the least amount of characters needed to determine if the string is multiline. - * - * If the result is false, returns the consumed character through the `excess` variable. - * - * Example 1: - * """test""" ==> """test""" - * ^ ^ - * - * Example 2: - * "test" ==> "test" (doesn't return the first quote) - * ^ ^ - * - * Example 3: - * "" ==> "" (returns the extra `"` through the `excess` variable) - * ^ ^ - */ - static bool IsTripleQuote(char quote, TextReader reader, out char excess) - { - // Copypasta, but it's faster... - - int cur; - // Consume the first quote - reader.Read(); - - if ((cur = reader.Peek()) < 0) - throw new TomlParseException("Unexpected end of file!", ParseState.KeyValuePair); - - if ((char)cur != quote) - { - excess = '\0'; - return false; - } - - // Consume the second quote - excess = (char)reader.Read(); - - if ((cur = reader.Peek()) < 0 || (char)cur != quote) return false; - - // Consume the final quote - reader.Read(); - - excess = '\0'; - return true; - } - - /** - * A convenience method to process a single character within a quote. - */ - static bool ProcessQuotedValueCharacter(char quote, - bool isNonLiteral, - char c, - int next, - StringBuilder sb, - ref bool escaped) - { - if (TomlSyntax.ShouldBeEscaped(c)) - throw new TomlParseException($"The character U+{(int)c:X8} must be escaped in a string!", - ParseState.KeyValuePair); - - if (escaped) - { - sb.Append(c); - escaped = false; - return false; - } - - if (c == quote) return true; - - if (isNonLiteral && c == TomlSyntax.ESCAPE_SYMBOL) - if (next >= 0 && (char)next == quote) - escaped = true; - - if (c == TomlSyntax.NEWLINE_CHARACTER) - throw new TomlParseException("Encountered newline in single line string!", ParseState.KeyValuePair); - - sb.Append(c); - return false; - } - - /** - * Reads a single-line string. - * Assumes the cursor is at the first character that belongs to the string. - * Consumes all characters that belong to the string (including the closing quote). - * - * Example: - * "test" ==> "test" - * ^ ^ - */ - static string ReadQuotedValueSingleLine(char quote, TextReader reader, char initialData = '\0') - { - var isNonLiteral = quote == TomlSyntax.BASIC_STRING_SYMBOL; - var sb = new StringBuilder(); - - var escaped = false; - - if (initialData != '\0' && - ProcessQuotedValueCharacter(quote, isNonLiteral, initialData, reader.Peek(), sb, ref escaped)) - return isNonLiteral ? sb.ToString().Unescape() : sb.ToString(); - - int cur; - while ((cur = reader.Read()) >= 0) - { - var c = (char)cur; - if (ProcessQuotedValueCharacter(quote, isNonLiteral, c, reader.Peek(), sb, ref escaped)) break; - } - - return isNonLiteral ? sb.ToString().Unescape() : sb.ToString(); - } - - /** - * Reads a multiline string. - * Assumes the cursor is at the first character that belongs to the string. - * Consumes all characters that belong to the string and the three closing quotes. - * - * Example: - * """test""" ==> """test""" - * ^ ^ - */ - static string ReadQuotedValueMultiLine(char quote, TextReader reader) - { - var isBasic = quote == TomlSyntax.BASIC_STRING_SYMBOL; - var sb = new StringBuilder(); - - var escaped = false; - var skipWhitespace = false; - var quotesEncountered = 0; - var first = true; - - int cur; - while ((cur = reader.Read()) >= 0) - { - var c = (char)cur; - - if (TomlSyntax.ShouldBeEscaped(c)) - throw new Exception($"The character U+{(int)c:X8} must be escaped!"); - - // Trim the first newline - if (first && TomlSyntax.IsNewLine(c)) - { - if (c != TomlSyntax.NEWLINE_CARRIAGE_RETURN_CHARACTER) first = false; - continue; - } - - first = false; - - //TODO: Reuse ProcessQuotedValueCharacter - - // Skip the current character if it is going to be escaped later - if (escaped) - { - sb.Append(c); - escaped = false; - continue; - } - - // If we are currently skipping empty spaces, skip - if (skipWhitespace) - { - if (TomlSyntax.IsEmptySpace(c)) continue; - skipWhitespace = false; - } - - // If we encounter an escape sequence... - if (isBasic && c == TomlSyntax.ESCAPE_SYMBOL) - { - var next = reader.Peek(); - if (next >= 0) - { - // ...and the next char is empty space, we must skip all whitespaces - if (TomlSyntax.IsEmptySpace((char)next)) - { - skipWhitespace = true; - continue; - } - - // ...and we have \", skip the character - if ((char)next == quote) escaped = true; - } - } - - // Count the consecutive quotes - if (c == quote) - quotesEncountered++; - else - quotesEncountered = 0; - - // If the are three quotes, count them as closing quotes - if (quotesEncountered == 3) break; - - sb.Append(c); - } - - // Remove last two quotes (third one wasn't included by default - sb.Length -= 2; - - return isBasic ? sb.ToString().Unescape() : sb.ToString(); - } - - #endregion - - #region Node creation - - static void InsertNode(TomlNode node, TomlNode root, List path) - { - var latestNode = root; - - if (path.Count > 1) - for (var index = 0; index < path.Count - 1; index++) - { - var subkey = path[index]; - if (latestNode.TryGetNode(subkey, out var currentNode)) - { - if (currentNode.HasValue) - throw new - TomlParseException($"The key {".".Join(path)} already has a value assigned to it!", - ParseState.KeyValuePair); - } - else - { - currentNode = new TomlTable - { - IsInline = true - }; - latestNode[subkey] = currentNode; - } - - latestNode = currentNode; - } - - if (latestNode.HasKey(path[path.Count - 1])) - throw new TomlParseException($"The key {".".Join(path)} is already defined!", ParseState.KeyValuePair); - - latestNode[path[path.Count - 1]] = node; - } - - static TomlTable CreateTable(TomlNode root, List path, bool arrayTable) - { - if (path.Count == 0) return null; - - var latestNode = root; - - for (var index = 0; index < path.Count; index++) - { - var subkey = path[index]; - - if (latestNode.TryGetNode(subkey, out var node)) - { - if (node.IsArray && arrayTable) - { - var arr = (TomlArray)node; - - if (!arr.IsTableArray) - throw new - TomlParseException($"The array {".".Join(path)} cannot be redefined as an array table!", - ParseState.Table); - - if (index == path.Count - 1) - { - latestNode = new TomlTable(); - arr.Add(latestNode); - break; - } - - latestNode = arr[arr.ChildrenCount - 1]; - continue; - } - - if (node.HasValue) - { - if (!(node is TomlArray array) || !array.IsTableArray) - throw new TomlParseException($"The key {".".Join(path)} has a value assigned to it!", - ParseState.Table); - - latestNode = array[array.ChildrenCount - 1]; - continue; - } - - if (index == path.Count - 1) - { - if (arrayTable && !node.IsArray) - throw new - TomlParseException($"The table {".".Join(path)} cannot be redefined as an array table!", - ParseState.Table); - if (node is TomlTable tbl && !tbl.IsInline) - throw new TomlParseException($"The table {".".Join(path)} is defined multiple times!", - ParseState.Table); - } - } - else - { - if (index == path.Count - 1 && arrayTable) - { - var table = new TomlTable(); - var arr = new TomlArray - { - IsTableArray = true - }; - arr.Add(table); - latestNode[subkey] = arr; - latestNode = table; - break; - } - - node = new TomlTable - { - IsInline = true - }; - latestNode[subkey] = node; - } - - latestNode = node; - } - - var result = (TomlTable)latestNode; - return result; - } - - #endregion - } - - #region Exception Types - - public class TomlException : Exception - { - public TomlException(string message) : base(message) { } - } - - public class TomlParseException : TomlException - { - public TomlParseException(string message, TOML.ParseState state) : base(message) => ParseState = state; - public TOML.ParseState ParseState { get; } - } - - public class TomlFormatException : TomlException - { - public TomlFormatException(string message) : base(message) { } - } - - #endregion - - #region Parse utilities - - static class TomlSyntax - { - #region Type Patterns - - public const string TRUE_VALUE = "true"; - public const string FALSE_VALUE = "false"; - public const string NAN_VALUE = "nan"; - public const string POS_NAN_VALUE = "+nan"; - public const string NEG_NAN_VALUE = "-nan"; - public const string INF_VALUE = "inf"; - public const string POS_INF_VALUE = "+inf"; - public const string NEG_INF_VALUE = "-inf"; - - public static bool IsBoolean(string s) => s == TRUE_VALUE || s == FALSE_VALUE; - - public static bool IsPosInf(string s) => s == INF_VALUE || s == POS_INF_VALUE; - - public static bool IsNegInf(string s) => s == NEG_INF_VALUE; - - public static bool IsNaN(string s) => s == NAN_VALUE || s == POS_NAN_VALUE || s == NEG_NAN_VALUE; - - public static bool IsInteger(string s) => IntegerPattern.IsMatch(s); - - public static bool IsFloat(string s) => FloatPattern.IsMatch(s); - - public static bool IsIntegerWithBase(string s, out int numberBase) - { - numberBase = 10; - var match = BasedIntegerPattern.Match(s); - if (!match.Success) return false; - IntegerBases.TryGetValue(match.Groups["base"].Value, out numberBase); - return true; - } - - /** - * A pattern to verify the integer value according to the TOML specification. - */ - public static readonly Regex IntegerPattern = - new Regex(@"^(\+|-)?(?!_)(0|(?!0)(_?\d)*)$", RegexOptions.Compiled); - - /** - * A pattern to verify a special 0x, 0o and 0b forms of an integer according to the TOML specification. - */ - public static readonly Regex BasedIntegerPattern = - new Regex(@"^(\+|-)?0(?x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - /** - * A pattern to verify the float value according to the TOML specification. - */ - public static readonly Regex FloatPattern = - new - Regex(@"^(\+|-)?(?!_)(0|(?!0)(_?\d)+)(((e(\+|-)?(?!_)(_?\d)+)?)|(\.(?!_)(_?\d)+(e(\+|-)?(?!_)(_?\d)+)?))$", - RegexOptions.Compiled | RegexOptions.IgnoreCase); - - /** - * A helper dictionary to map TOML base codes into the radii. - */ - public static readonly Dictionary IntegerBases = new Dictionary - { - ["x"] = 16, - ["o"] = 8, - ["b"] = 2 - }; - - /** - * A helper dictionary to map non-decimal bases to their TOML identifiers - */ - public static readonly Dictionary BaseIdentifiers = new Dictionary - { - [2] = "b", - [8] = "o", - [16] = "x" - }; - - /** - * Valid date formats with timezone as per RFC3339. - */ - public static readonly string[] RFC3339Formats = - { - "yyyy'-'MM-dd HH':'mm':'ssK", "yyyy'-'MM-dd HH':'mm':'ss'.'fK", "yyyy'-'MM-dd HH':'mm':'ss'.'ffK", - "yyyy'-'MM-dd HH':'mm':'ss'.'fffK", "yyyy'-'MM-dd HH':'mm':'ss'.'ffffK", - "yyyy'-'MM-dd HH':'mm':'ss'.'fffffK", "yyyy'-'MM-dd HH':'mm':'ss'.'ffffffK", - "yyyy'-'MM-dd HH':'mm':'ss'.'fffffffK" - }; - - /** - * Valid date formats without timezone (assumes local) as per RFC3339. - */ - public static readonly string[] RFC3339LocalDateTimeFormats = - { - "yyyy'-'MM-dd HH':'mm':'ss", "yyyy'-'MM-dd HH':'mm':'ss'.'f", "yyyy'-'MM-dd HH':'mm':'ss'.'ff", - "yyyy'-'MM-dd HH':'mm':'ss'.'fff", "yyyy'-'MM-dd HH':'mm':'ss'.'ffff", - "yyyy'-'MM-dd HH':'mm':'ss'.'fffff", "yyyy'-'MM-dd HH':'mm':'ss'.'ffffff", - "yyyy'-'MM-dd HH':'mm':'ss'.'fffffff" - }; - - /** - * Valid full date format as per TOML spec. - */ - public static readonly string LocalDateFormat = "yyyy'-'MM'-'dd"; - - /** - * Valid time formats as per TOML spec. - */ - public static readonly string[] RFC3339LocalTimeFormats = - { - "HH':'mm':'ss", "HH':'mm':'ss'.'f", "HH':'mm':'ss'.'ff", "HH':'mm':'ss'.'fff", "HH':'mm':'ss'.'ffff", - "HH':'mm':'ss'.'fffff", "HH':'mm':'ss'.'ffffff", "HH':'mm':'ss'.'fffffff" - }; - - #endregion - - #region Character definitions - - public const char ARRAY_END_SYMBOL = ']'; - public const char ITEM_SEPARATOR = ','; - public const char ARRAY_START_SYMBOL = '['; - public const char BASIC_STRING_SYMBOL = '\"'; - public const char COMMENT_SYMBOL = '#'; - public const char ESCAPE_SYMBOL = '\\'; - public const char KEY_VALUE_SEPARATOR = '='; - public const char NEWLINE_CARRIAGE_RETURN_CHARACTER = '\r'; - public const char NEWLINE_CHARACTER = '\n'; - public const char SUBKEY_SEPARATOR = '.'; - public const char TABLE_END_SYMBOL = ']'; - public const char TABLE_START_SYMBOL = '['; - public const char INLINE_TABLE_START_SYMBOL = '{'; - public const char INLINE_TABLE_END_SYMBOL = '}'; - public const char LITERAL_STRING_SYMBOL = '\''; - public const char INT_NUMBER_SEPARATOR = '_'; - - public static readonly char[] NewLineCharacters = { NEWLINE_CHARACTER, NEWLINE_CARRIAGE_RETURN_CHARACTER }; - - - public static bool IsQuoted(char c) => c == BASIC_STRING_SYMBOL || c == LITERAL_STRING_SYMBOL; - - public static bool IsWhiteSpace(char c) => c == ' ' || c == '\t'; - - public static bool IsNewLine(char c) => c == NEWLINE_CHARACTER || c == NEWLINE_CARRIAGE_RETURN_CHARACTER; - - public static bool IsEmptySpace(char c) => IsWhiteSpace(c) || IsNewLine(c); - - public static bool IsBareKey(char c) => - 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c == '-'; - - public static bool ShouldBeEscaped(char c) => (c <= '\u001f' || c == '\u007f') && !IsNewLine(c); - - public static bool IsValueSeparator(char c) => - c == ITEM_SEPARATOR || c == ARRAY_END_SYMBOL || c == INLINE_TABLE_END_SYMBOL; - - #endregion - } - - static class StringUtils - { - public static string AsKey(this string key) - { - var quote = false; - foreach (var c in key) - { - if (TomlSyntax.IsBareKey(c)) continue; - quote = true; - break; - } - return !quote ? key : $"{TomlSyntax.BASIC_STRING_SYMBOL}{key.Escape()}{TomlSyntax.BASIC_STRING_SYMBOL}"; - } - - public static string Join(this string self, IEnumerable subItems) - { - var sb = new StringBuilder(); - var first = true; - - foreach (var subItem in subItems) - { - if (!first) sb.Append(self); - first = false; - sb.Append(subItem); - } - - return sb.ToString(); - } - - public static bool TryParseDateTime(string s, - string[] formats, - DateTimeStyles styles, - out DateTime dateTime, - out int parsedFormat) - { - parsedFormat = 0; - dateTime = new DateTime(); - - for (var i = 0; i < formats.Length; i++) - { - var format = formats[i]; - if (!DateTime.TryParseExact(s, format, CultureInfo.InvariantCulture, styles, out dateTime)) continue; - parsedFormat = i; - return true; - } - - return false; - } - - public static void AsComment(this string self, TextWriter tw) - { - foreach (var line in self.Split(TomlSyntax.NEWLINE_CHARACTER)) - tw.WriteLine($"{TomlSyntax.COMMENT_SYMBOL} {line.Trim()}"); - } - - public static string RemoveAll(this string txt, char toRemove) - { - var sb = new StringBuilder(txt.Length); - - foreach (var c in txt) - if (c != toRemove) - sb.Append(c); - - return sb.ToString(); - } - - public static string Escape(this string txt, bool escapeNewlines = true) - { - var stringBuilder = new StringBuilder(txt.Length + 2); - for (var i = 0; i < txt.Length; i++) - { - var c = txt[i]; - switch (c) - { - case '\b': - stringBuilder.Append(@"\b"); - break; - case '\t': - stringBuilder.Append(@"\t"); - break; - case '\n' when escapeNewlines: - stringBuilder.Append(@"\n"); - break; - case '\f': - stringBuilder.Append(@"\f"); - break; - case '\r' when escapeNewlines: - stringBuilder.Append(@"\r"); - break; - case '\\': - stringBuilder.Append(@"\"); - break; - case '\"': - stringBuilder.Append(@"\"""); - break; - default: - if (TomlSyntax.ShouldBeEscaped(c) || TOML.ForceASCII && c > sbyte.MaxValue) - { - if (char.IsSurrogatePair(txt, i)) - stringBuilder.Append("\\U").Append(char.ConvertToUtf32(txt, i++).ToString("X8")); - else - stringBuilder.Append("\\u").Append(((ushort)c).ToString("X4")); - } - else - stringBuilder.Append(c); - break; - } - } - - return stringBuilder.ToString(); - } - - - public static string Unescape(this string txt) - { - if (string.IsNullOrEmpty(txt)) return txt; - var stringBuilder = new StringBuilder(txt.Length); - for (var i = 0; i < txt.Length;) - { - var num = txt.IndexOf('\\', i); - var next = num + 1; - if (num < 0 || num == txt.Length - 1) num = txt.Length; - stringBuilder.Append(txt, i, num - i); - if (num >= txt.Length) break; - var c = txt[next]; - switch (c) - { - case 'b': - stringBuilder.Append('\b'); - break; - case 't': - stringBuilder.Append('\t'); - break; - case 'n': - stringBuilder.Append('\n'); - break; - case 'f': - stringBuilder.Append('\f'); - break; - case 'r': - stringBuilder.Append('\r'); - break; - case '\'': - stringBuilder.Append('\''); - break; - case '\"': - stringBuilder.Append('\"'); - break; - case '\\': - stringBuilder.Append('\\'); - break; - case 'u': - if (next + 4 >= txt.Length) throw new Exception("Undefined escape sequence!"); - stringBuilder.Append(char.ConvertFromUtf32(Convert.ToInt32(txt.Substring(next + 1, 4), 16))); - num += 4; - break; - case 'U': - if (next + 8 >= txt.Length) throw new Exception("Undefined escape sequence!"); - stringBuilder.Append(char.ConvertFromUtf32(Convert.ToInt32(txt.Substring(next + 1, 8), 16))); - num += 8; - break; - default: - throw new Exception("Undefined escape sequence!"); - } - - i = num + 2; - } - - return stringBuilder.ToString(); - } - } - - #endregion -} \ No newline at end of file diff --git a/src/Misc/Common.cs b/src/Misc/Common.cs new file mode 100644 index 0000000..6b7b650 --- /dev/null +++ b/src/Misc/Common.cs @@ -0,0 +1,69 @@ + +using System.Collections.Generic; +using System.Linq; + +namespace mpvnet +{ + public class StringPair + { + public string Name { get; set; } + public string Value { get; set; } + } + + public class ConfParser + { + public static List Parse(string content) + { + string[] lines = content.Split("\r\n".ToCharArray()); + var sections = new List(); + ConfSection currentGroup = null; + + foreach (string i in lines) + { + string line = i.Trim(); + + if (string.IsNullOrEmpty(line)) + continue; + + if (line.StartsWith("[") && line.EndsWith("]")) + { + currentGroup = new ConfSection() { Name = line.TrimStart('[').TrimEnd(']') }; + sections.Add(currentGroup); + } + else if (line.Contains("=")) + { + string name = line.Substring(0, line.IndexOf("=")).Trim(); + string value = line.Substring(line.IndexOf("=") + 1).Trim(); + + currentGroup.Items.Add(new StringPair() { Name = name, Value = value }); + } + } + + return sections; + } + } + + public class ConfSection + { + public string Name { get; set; } + public List Items { get; set; } = new List(); + + public bool HasName(string name) + { + foreach (var i in Items) + if (i.Name == name) + return true; + return false; + } + + public string GetValue(string name) + { + foreach (var i in Items) + if (i.Name == name) + return i.Value; + return null; + } + + public List GetValues(string name) => Items.Where(i => i.Name == name).ToList(); + } +} diff --git a/src/DynamicGUI/DynamicGUI.cs b/src/Misc/Conf.cs similarity index 69% rename from src/DynamicGUI/DynamicGUI.cs rename to src/Misc/Conf.cs index 5994094..300eb11 100644 --- a/src/DynamicGUI/DynamicGUI.cs +++ b/src/Misc/Conf.cs @@ -1,49 +1,42 @@  using System; using System.Collections.Generic; -using System.IO; using System.Windows; using System.Windows.Documents; using System.Windows.Navigation; -using Tommy; -using mpvnet; - -namespace DynamicGUI +namespace mpvnet { - public class Settings + public class Conf { - public static List LoadSettings(string content) + public static List LoadConf(string content) { - TomlTable table; + List settingsList = new List(); - using (StringReader reader = new StringReader(content)) - table = TOML.Parse(reader); - - List settingsList = new List(); - - foreach (TomlTable setting in table["settings"]) + foreach (ConfSection section in ConfParser.Parse(content)) { SettingBase baseSetting = null; - if (setting.HasKey("options")) + if (section.HasName("option")) { OptionSetting optionSetting = new OptionSetting(); baseSetting = optionSetting; - optionSetting.Default = setting["default"]; + optionSetting.Default = section.GetValue("default"); optionSetting.Value = optionSetting.Default; - foreach (TomlTable option in setting["options"]) + foreach (var i in section.GetValues("option")) { var opt = new OptionSettingOption(); - opt.Name = option["name"]; - if (option.HasKey("help")) - opt.Help = option["help"]; + if (i.Value.Contains(" ")) + { + opt.Name = i.Value.Substring(0, i.Value.IndexOf(" ")); + opt.Help = i.Value.Substring(i.Value.IndexOf(" ")).Trim(); + } + else + opt.Name = i.Value; - if (option.HasKey("text")) - opt.Text = option["text"]; - else if (opt.Name == optionSetting.Default) + if (opt.Name == optionSetting.Default) opt.Text = opt.Name + " (Default)"; opt.OptionSetting = optionSetting; @@ -54,20 +47,21 @@ namespace DynamicGUI { StringSetting stringSetting = new StringSetting(); baseSetting = stringSetting; - stringSetting.Default = setting.HasKey("default") ? setting["default"].ToString() : ""; + stringSetting.Default = section.HasName("default") ? section.GetValue("default") : ""; } - baseSetting.Name = setting["name"]; - baseSetting.File = setting["file"]; - baseSetting.Filter = setting["filter"]; + baseSetting.Name = section.GetValue("name"); + baseSetting.File = section.GetValue("file"); + baseSetting.Filter = section.GetValue("filter"); - if (setting.HasKey("help")) baseSetting.Help = setting["help"]; - if (setting.HasKey("url")) baseSetting.URL = setting["url"]; - if (setting.HasKey("width")) baseSetting.Width = setting["width"]; - if (setting.HasKey("type")) baseSetting.Type = setting["type"]; + if (section.HasName("help")) baseSetting.Help = section.GetValue("help"); + if (section.HasName("url")) baseSetting.URL = section.GetValue("url"); + if (section.HasName("width")) baseSetting.Width = Convert.ToInt32(section.GetValue("width")); + if (section.HasName("type")) baseSetting.Type = section.GetValue("type"); settingsList.Add(baseSetting); } + return settingsList; } } @@ -155,11 +149,13 @@ namespace DynamicGUI public void SetURL(string url) { - if (string.IsNullOrEmpty(url)) return; + if (string.IsNullOrEmpty(url)) + return; + NavigateUri = new Uri(url); RequestNavigate += HyperLinkEx_RequestNavigate; Inlines.Clear(); Inlines.Add(url); } } -} \ No newline at end of file +} diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index 3cf86f3..6781eeb 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -62,19 +62,21 @@ namespace mpvnet.Properties { /// /// Looks up a localized string similar to - ///[[settings]] - ///name = "hwdec" - ///file = "mpv" - ///default = "no" - ///filter = "Video" - ///url = "https://mpv.io/manual/master/#options-hwdec" - ///help = "Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:" - ///options = [{ name = "no", help = "always use software decoding" }, - /// { name = "auto", h [rest of string was truncated]";. + ///[setting] + ///name = hwdec + ///file = mpv + ///default = no + ///filter = Video + ///url = https://mpv.io/manual/master/#options-hwdec + ///help = Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit: + /// + ///option = no always use software decoding + ///option = auto enable best hw decoder (see below) + ///option = yes [rest of string was truncated]";. /// - internal static string editor_toml { + internal static string editor_conf { get { - return ResourceManager.GetString("editor_toml", resourceCulture); + return ResourceManager.GetString("editor_conf", resourceCulture); } } @@ -110,7 +112,7 @@ namespace mpvnet.Properties { ///keep-open-pause = no ///osd-duration = 2000 ///osd-playing-msg = '${filename}' - ///script-opts = osc-scalewindowed=1.5,osc-hidetimeout=2000,osc-greenandgrumpy=yes,console-scale=1 + ///script-opts = osc-scalewindowed=1.5,osc-hidetimeout=2000,console-scale=1 ///screenshot-directory = '~~desktop/' /// ///[protocol.https] diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx index e55dfa1..46fa61b 100644 --- a/src/Properties/Resources.resx +++ b/src/Properties/Resources.resx @@ -118,8 +118,8 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Resources\editor.toml.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + ..\Resources\editor_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 ..\Resources\input.conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 diff --git a/src/Resources/editor.toml.txt b/src/Resources/editor.toml.txt deleted file mode 100644 index f5ee32f..0000000 --- a/src/Resources/editor.toml.txt +++ /dev/null @@ -1,618 +0,0 @@ - -[[settings]] -name = "hwdec" -file = "mpv" -default = "no" -filter = "Video" -url = "https://mpv.io/manual/master/#options-hwdec" -help = "Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:" -options = [{ name = "no", help = "always use software decoding" }, - { name = "auto", help = "enable best hw decoder (see below)" }, - { name = "yes", help = "exactly the same as auto" }, - { name = "auto-copy", help = "enable best hw decoder with copy-back (see below)" }, - { name = "dxva2", help = "requires vo=gpu with gpu-context=d3d11, gpu-context=angle or gpu-context=dxinterop (Windows only)" }, - { name = "dxva2-copy", help = "copies video back to system RAM (Windows only)" }, - { name = "d3d11va", help = "requires vo=gpu with gpu-context=d3d11 or gpu-context=angle (Windows 8+ only)" }, - { name = "d3d11va-copy", help = "copies video back to system RAM (Windows 8+ only)" }, - { name = "cuda", help = "requires vo=gpu (Any platform CUDA is available)" }, - { name = "cuda-copy", help = "copies video back to system RAM (Any platform CUDA is available)" }, - { name = "nvdec", help = "requires vo=gpu (Any platform CUDA is available)" }, - { name = "nvdec-copy", help = "copies video back to system RAM (Any platform CUDA is available)" }, - { name = "crystalhd", help = "copies video back to system RAM (Any platform supported by hardware)" }, - { name = "rkmpp", help = "requires vo=gpu (some RockChip devices only)" }] - -[[settings]] -name = "gpu-api" -file = "mpv" -default = "auto" -filter = "Video" -help = "Controls which type of graphics APIs will be accepted. Auto uses d3d11, it should only be changed in case of problems, Vulkan is not recommended." -options = [{ name = "auto", help = "Use any available API" }, - { name = "opengl", help = "Allow only OpenGL (requires OpenGL 2.1+ or GLES 2.0+)" }, - { name = "vulkan", help = "Allow only Vulkan (not recommended). " }, - { name = "d3d11", help = "Allow only gpu-context=d3d11" }] - -[[settings]] -name = "gpu-context" -file = "mpv" -default = "auto" -filter = "Video" -options = [{ name = "auto", help = "auto-select" }, - { name = "win", help = "Win32/WGL" }, - { name = "winvk", help = "VK_KHR_win32_surface" }, - { name = "angle", help = "Direct3D11 through the OpenGL ES translation layer ANGLE. This supports almost everything the win backend does (if the ANGLE build is new enough)." }, - { name = "dxinterop", help = "(experimental) Win32, using WGL for rendering and Direct3D 9Ex for presentation. Works on Nvidia and AMD. Newer Intel chips with the latest drivers may also work." }, - { name = "d3d11", help = "Win32, with native Direct3D 11 rendering." }] - -[[settings]] -name = "vo" -file = "mpv" -default = "gpu" -filter = "Video" -help = "Video output drivers to be used.\n\nFor more information visit:" -url = "https://mpv.io/manual/master/#video-output-drivers-vo" -options = [{ name = "gpu", help = "General purpose, customizable, GPU-accelerated video output driver. It supports extended scaling methods, dithering, color management, custom shaders, HDR, and more." }, - { name = "direct3d", help = "Video output driver that uses the Direct3D interface" }] - -[[settings]] -name = "video-sync" -file = "mpv" -default = "audio" -filter = "Video" -help = "How the player synchronizes audio and video.\n\nFor more information visit:" -url = "https://mpv.io/manual/master/#options-video-sync" -options = [{ name = "audio" }, - { name = "display-resample" }, - { name = "display-resample-vdrop" }, - { name = "display-resample-desync" }, - { name = "display-vdrop" }, - { name = "display-adrop" }, - { name = "display-desync" }, - { name = "desync" }] - -[[settings]] -name = "scale" -file = "mpv" -default = "bilinear" -filter = "Video" -help = "The GPU renderer filter function to use when upscaling video. There are some more filters, but most are not as useful. For a complete list, pass help as value, e.g.: mpv --scale=help" -options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality)." }, - { name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." }, - { name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" }, - { name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" }, - { name = "ewa_lanczossharp", help = "A slightly sharpened version of ewa_lanczos, preconfigured to use an ideal radius and parameter. If your hardware can run it, this is probably what you should use by default." }, - { name = "mitchell", help = "Mitchell-Netravali. The B and C parameters can be set with scale-param1 and scale-param2. This filter is very good at downscaling (see dscale)." }, - { name = "oversample", help = "A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of rounded. This essentially removes the small imperfections and judder artifacts caused by nearest-neighbour interpolation, in exchange for adding some blur. This filter is good at temporal interpolation, and also known as \"smoothmotion\" (see tscale)." }] - -[[settings]] -name = "cscale" -file = "mpv" -default = "bilinear" -filter = "Video" -help = "As scale, but for interpolating chroma information. If the image is not subsampled, this option is ignored entirely." -options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality)." }, - { name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." }, - { name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" }, - { name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" }, - { name = "ewa_lanczossharp", help = "A slightly sharpened version of ewa_lanczos, preconfigured to use an ideal radius and parameter. If your hardware can run it, this is probably what you should use by default." }, - { name = "mitchell", help = "Mitchell-Netravali. The B and C parameters can be set with scale-param1 and scale-param2. This filter is very good at downscaling (see dscale)." }, - { name = "oversample", help = "A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of rounded. This essentially removes the small imperfections and judder artifacts caused by nearest-neighbour interpolation, in exchange for adding some blur. This filter is good at temporal interpolation, and also known as \"smoothmotion\" (see tscale)." }] - -[[settings]] -name = "dscale" -file = "mpv" -default = "auto" -filter = "Video" -help = "Like scale, but apply these filters on downscaling instead." -options = [{ name = "auto", help = "Same with the upscaler." }, - { name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality)." }, - { name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." }, - { name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" }, - { name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" }, - { name = "ewa_lanczossharp", help = "A slightly sharpened version of ewa_lanczos, preconfigured to use an ideal radius and parameter. If your hardware can run it, this is probably what you should use by default." }, - { name = "mitchell", help = "Mitchell-Netravali. The B and C parameters can be set with scale-param1 and scale-param2. This filter is very good at downscaling (see dscale)." }, - { name = "oversample", help = "A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of rounded. This essentially removes the small imperfections and judder artifacts caused by nearest-neighbour interpolation, in exchange for adding some blur. This filter is good at temporal interpolation, and also known as \"smoothmotion\" (see tscale)." }] - -[[settings]] -name = "dither-depth" -file = "mpv" -default = "no" -filter = "Video" -help = "Set dither target depth to N. Note that the depth of the connected video display device cannot be detected. Often, LCD panels will do dithering on their own, which conflicts with this option and leads to ugly output." -options = [{ name = "no", help = "Disable any dithering done by mpv." }, - { name = "auto", help = "Automatic selection. If output bit depth cannot be detected, 8 bits per component are assumed." }, - { name = "8", help = "Dither to 8 bit output." }, - { name = "10", help = "Dither to 10 bit output." }] - -[[settings]] -name = "correct-downscaling" -file = "mpv" -default = "no" -filter = "Video" -help = "When using convolution based filters, extend the filter size when downscaling. Increases quality, but reduces performance while downscaling.\n\nThis will perform slightly sub-optimally for anamorphic video (but still better than without it) since it will extend the size to match only the milder of the scale factors between the axes." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "sigmoid-upscaling" -file = "mpv" -default = "no" -filter = "Video" -help = "When upscaling, use a sigmoidal color transform to avoid emphasizing ringing artifacts. This also implies linear-scaling." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "deband" -file = "mpv" -default = "no" -filter = "Video" -help = "Enable the debanding algorithm. This greatly reduces the amount of visible banding, blocking and other quantization artifacts, at the expense of very slightly blurring some of the finest details. In practice, it's virtually always an improvement - the only reason to disable it would be for performance." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "d3d11va-zero-copy" -file = "mpv" -default = "no" -filter = "Video" -help = "By default, when using hardware decoding with --gpu-api=d3d11, the video image will be copied (GPU-to-GPU) from the decoder surface to a shader resource. Set this option to avoid that copy by sampling directly from the decoder image. This may increase performance and reduce power usage, but can cause the image to be sampled incorrectly on the bottom and right edges due to padding, and may invoke driver bugs, since Direct3D 11 technically does not allow sampling from a decoder surface (though most drivers support it.)" -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "hdr-compute-peak" -file = "mpv" -default = "auto" -filter = "Video" -help = "Compute the HDR peak and frame average brightness per-frame instead of relying on tagged metadata. These values are averaged over local regions as well as over several frames to prevent the value from jittering around too much. This option basically gives you dynamic, per-scene tone mapping. Requires compute shaders, which is a fairly recent OpenGL feature, and will probably also perform horribly on some drivers, so enable at your own risk. The special value auto (default) will enable HDR peak computation automatically if compute shaders and SSBOs are supported." -options = [{ name = "auto" }, - { name = "yes" }, - { name = "no" }] - -[[settings]] -name = "volume" -file = "mpv" -filter = "Audio" -help = "Set the startup volume. 0 means silence, 100 means no volume reduction or amplification. Negative values can be passed for compatibility, but are treated as 0. Since mpv 0.18.1, this always controls the internal mixer (aka \"softvol\"). Default: 100" - -[[settings]] -name = "remember-volume" -file = "mpvnet" -default = "yes" -filter = "Audio" -help = "Save volume and mute on exit and restore it on start. (mpv.net specific setting)" -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "alang" -file = "mpv" -filter = "Audio" -type = "string" -help = "Specify a priority list of audio languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two-letter language codes, Matroska, MPEG-TS and NUT use ISO 639-2 three-letter language codes, while OGM uses a free-form identifier. See also aid.\n\nExamples\n\nmpv dvd://1 alang=hu,en chooses the Hungarian language track on a DVD and falls back on English if Hungarian is not available.\n\nmpv alang=jpn example.mkv plays a Matroska file with Japanese audio." - -[[settings]] -name = "audio-file-auto" -file = "mpv" -default = "no" -filter = "Audio" -help = "Load additional audio files matching the video filename. The parameter specifies how external audio files are matched." -options = [{ name = "no", help = "Don't automatically load external audio files." }, - { name = "exact", help = "Load the media filename with audio file extension." }, - { name = "fuzzy", help = "Load all audio files containing media filename." }, - { name = "all", help = "Load all audio files in the current and audio-file-paths directories." }] - -[[settings]] -name = "audio-device" -file = "mpv" -filter = "Audio" -type = "string" -url = "https://mpv.io/manual/master/#options-audio-device" -help = " Use the given audio device. This consists of the audio output name, e.g. alsa, followed by /, followed by the audio output specific device name. The default value for this option is auto, which tries every audio output in preference order with the default device.\nAvailable devices can be found in the mpv.net context menu under:\nView > Show Audio Devices" - -[[settings]] -name = "slang" -file = "mpv" -filter = "Subtitle" -type = "string" -help = "Specify a priority list of subtitle languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two letter language codes, Matroska uses ISO 639-2 three letter language codes while OGM uses a free-form identifier. See also sid." - -[[settings]] -name = "sub-auto" -file = "mpv" -default = "exact" -filter = "Subtitle" -help = "Load additional subtitle files matching the video filename. The parameter specifies how external subtitle files are matched. exact is enabled by default." -options = [{ name = "no", help = "Don't automatically load external subtitle files." }, - { name = "exact", help = "Load the media filename with subtitle file extension." }, - { name = "fuzzy", help = "Load all subs containing media filename." }, - { name = "all", help = "Load all subs in the current and sub-file-paths directories." }] - -[[settings]] -name = "sub-font" -file = "mpv" -filter = "Subtitle" -type = "string" -help = "Specify font to use for subtitles that do not themselves specify a particular font. The default is sans-serif." - -[[settings]] -name = "sub-font-size" -file = "mpv" -filter = "Subtitle" -help = "Specify the sub font size. The unit is the size in scaled pixels at a window height of 720. The actual pixel size is scaled with the window height: if the window height is larger or smaller than 720, the actual size of the text increases or decreases as well. Default: 55" - -[[settings]] -name = "sub-color" -file = "mpv" -type = "color" -filter = "Subtitle" -help = "Specify the color used for unstyled text subtitles.\n\nThe color is specified in the form r/g/b, where each color component is specified as number in the range 0.0 to 1.0. It's also possible to specify the transparency by using r/g/b/a, where the alpha value 0 means fully transparent, and 1.0 means opaque. If the alpha component is not given, the color is 100% opaque.\n\nPassing a single number to the option sets the sub to gray, and the form gray/a lets you specify alpha additionally.\n\nExamples\n\n1.0/0.0/0.0 set sub to opaque red\n1.0/0.0/0.0/0.75 set sub to opaque red with 75% alpha\n0.5/0.75 set sub to 50% gray with 75% alpha\n\nAlternatively, the color can be specified as a RGB hex triplet in the form #RRGGBB, where each 2-digit group expresses a color value in the range 0 (00) to 255 (FF). For example, #FF0000 is red. This is similar to web colors. Alpha is given with #AARRGGBB.\n\nExamples\n\n#FF0000 set sub to opaque red\n#C0808080 set sub to 50% gray with 75% alpha" - -[[settings]] -name = "sub-border-color" -file = "mpv" -type = "color" -filter = "Subtitle" -help = "See sub-color. Color used for the sub font border. Ignored when sub-back-color is specified (or more exactly: when that option is not set to completely transparent)." - -[[settings]] -name = "sub-back-color" -file = "mpv" -type = "color" -filter = "Subtitle" -help = "See sub-color. Color used for sub text background. You can use sub-shadow-offset to change its size relative to the text." - -[[settings]] -name = "fullscreen" -file = "mpv" -default = "no" -filter = "Screen" -help = "Start the player in fullscreen mode." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "border" -file = "mpv" -default = "yes" -filter = "Screen" -help = "Show window with decoration (titlebar, border)." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "screen" -file = "mpv" -filter = "Screen" -help = "<0-32> In multi-monitor configurations (i.e. a single desktop that spans across multiple displays), this option tells mpv which screen to display the video on." - -[[settings]] -name = "osd-playing-msg" -file = "mpv" -width = 300 -filter = "Screen" -type = "string" -help = "Show a message on OSD when playback starts. The string is expanded for properties, e.g. osd-playing-msg='file: ${filename}' will show the message file: followed by a space and the currently played filename. For more information visit:" -url = "https://mpv.io/manual/master/#property-expansion" - -[[settings]] -name = "osd-font-size" -file = "mpv" -filter = "Screen" -help = "Specify the OSD font size. See sub-font-size for details. Default: 55" - -[[settings]] -name = "osd-duration" -file = "mpv" -filter = "Screen" -help = "Set the duration of the OSD messages in ms. Default: 1000" - -[[settings]] -name = "osd-scale-by-window" -file = "mpv" -default = "yes" -filter = "Screen" -help = "Whether to scale the OSD with the window size. If this is disabled, osd-font-size and other OSD options that use scaled pixels are always in actual pixels. The effect is that changing the window size won't change the OSD font size." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "autofit" -file = "mpv" -filter = "Screen" -help = " Initial window height in percent. Default: 60" - -[[settings]] -name = "autofit-smaller" -file = "mpv" -filter = "Screen" -help = " Minimum window height in percent. Default: 10" - -[[settings]] -name = "autofit-larger" -file = "mpv" -filter = "Screen" -help = " Maximum window height in percent. Default: 80" - -[[settings]] -name = "start-size" -file = "mpvnet" -default = "height-session" -filter = "Screen" -help = "Setting to remember the window size. (mpv.net specific setting)" -options = [{ name = "video", help = "Window size is set to video resolution" }, - { name = "width-session", help = "Width is remembered in the current session" }, - { name = "width-always", help = "Width is always remembered" }, - { name = "height-session", help = "Height is remembered in the current session" }, - { name = "height-always", help = "Height is always remembered" }, - { name = "always", help = "Size is always remembered" }] - -[[settings]] -name = "start-threshold" -file = "mpvnet" -filter = "Screen" -help = "Threshold in milliseconds to wait for libmpv returning the video resolution before the window is shown, otherwise default dimensions are used as defined by autofit and start-size. Default: 1500 (mpv.net specific setting)" - -[[settings]] -name = "minimum-aspect-ratio" -file = "mpvnet" -filter = "Screen" -help = " Minimum aspect ratio, if the AR is smaller than the defined value then the window AR is set to 16/9. This avoids a square window for Music with cover art. Default: 1.2 (mpv.net specific setting)" - -[[settings]] -name = "remember-window-position" -file = "mpvnet" -default = "no" -filter = "Screen" -help = "Save the window position on exit. (mpv.net specific setting)" -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "window-maximized" -file = "mpv" -default = "no" -filter = "Screen" -help = "Start with a maximized window." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "screenshot-directory" -file = "mpv" -width = 500 -type = "folder" -filter = "Screen" -help = "Store screenshots in this directory. This path is joined with the filename generated by screenshot-template. If the template filename is already absolute, the directory is ignored.\n\nIf the directory does not exist, it is created on the first screenshot. If it is not a directory, an error is generated when trying to write a screenshot." - -[[settings]] -name = "screenshot-format" -file = "mpv" -default = "jpg" -filter = "Screen" -help = "Set the image file type used for saving screenshots." -options = [{ name = "jpg" }, - { name = "png" }] - -[[settings]] -name = "screenshot-tag-colorspace" -file = "mpv" -default = "no" -filter = "Screen" -help = "Tag screenshots with the appropriate colorspace. Note that not all formats are supported." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "screenshot-high-bit-depth" -file = "mpv" -default = "yes" -filter = "Screen" -help = "If possible, write screenshots with a bit depth similar to the source video. This is interesting in particular for PNG, as this sometimes triggers writing 16 bit PNGs with huge file sizes. This will also include an unused alpha channel in the resulting files if 16 bit is used." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "screenshot-jpeg-source-chroma" -file = "mpv" -default = "yes" -filter = "Screen" -help = "Write JPEG files with the same chroma subsampling as the video. If disabled, the libjpeg default is used." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "screenshot-template" -file = "mpv" -filter = "Screen" -type = "string" -help = "Specify the filename template used to save screenshots. The template specifies the filename without file extension, and can contain format specifiers, which will be substituted when taking a screenshot. By default, the template is mpv-shot%n, which results in filenames like mpv-shot0012.png for example.\n\nFind the full documentation here:" -url = "https://mpv.io/manual/master/#options-screenshot-template" - -[[settings]] -name = "screenshot-jpeg-quality" -file = "mpv" -filter = "Screen" -help = "<0-100> Set the JPEG quality level. Higher means better quality. The default is 90." - -[[settings]] -name = "screenshot-png-compression" -file = "mpv" -filter = "Screen" -help = "<0-9> Set the PNG compression level. Higher means better compression. This will affect the file size of the written screenshot file and the time it takes to write a screenshot. Too high compression might occupy enough CPU time to interrupt playback. The default is 7." - -[[settings]] -name = "screenshot-png-filter" -file = "mpv" -filter = "Screen" -help = "<0-5> Set the filter applied prior to PNG compression. 0 is none, 1 is 'sub', 2 is 'up', 3 is 'average', 4 is 'Paeth', and 5 is 'mixed'. This affects the level of compression that can be achieved. For most images, 'mixed' achieves the best compression ratio, hence it is the default." - -[[settings]] -name = "taskbar-progress" -file = "mpv" -default = "yes" -filter = "Playback" -help = "Show progress in taskbar." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "keep-open-pause" -file = "mpv" -default = "yes" -filter = "Playback" -help = "If set to no, instead of pausing when keep-open is active, just stop at end of file and continue playing forward when you seek backwards until end where it stops again." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "keep-open" -file = "mpv" -default = "no" -filter = "Playback" -help = "Using no, mpv would terminate after the last file but mpv.net never terminates automatically." -options = [{ name = "yes", help = "If the current file ends, go to the next file, keep the last file open."}, - { name = "no", help = "If the current file ends, go to the next file." }, - { name = "always", help = "Playback will never automatically advance to the next file."}] - -[[settings]] -name = "loop-file" -file = "mpv" -filter = "Playback" -help = " Loop a single file N times. inf means forever, no means normal playback.\n\nThe difference to loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file. loop is an alias for this option." - -[[settings]] -name = "save-position-on-quit" -file = "mpv" -default = "no" -filter = "Playback" -help = "Always save the current playback position on quit. When this file is played again later, the player will seek to the old playback position on start. This does not happen if playback of a file is stopped in any other way than quitting. For example, going to the next file in the playlist will not save the position, and start playback at beginning the next time the file is played.\n\nThis behavior is disabled by default, but is always available when quitting the player with Shift+Q." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "hr-seek" -file = "mpv" -default = "absolute" -filter = "Playback" -help = "Select when to use precise seeks that are not limited to keyframes. Such seeks require decoding video from the previous keyframe up to the target position and so can take some time depending on decoding performance. For some video formats, precise seeks are disabled. This option selects the default choice to use for seeks; it is possible to explicitly override that default in the definition of key bindings and in input commands." -options = [{ name = "yes", help = "Use precise seeks whenever possible." }, - { name = "no", help = "Never use precise seeks." }, - { name = "absolute", help = "Use precise seeks if the seek is to an absolute position in the file, such as a chapter seek, but not for relative seeks like the default behavior of arrow keys." }, - { name = "always", help = "Same as yes (for compatibility)." }] - -[[settings]] -name = "track-auto-selection" -file = "mpv" -default = "yes" -filter = "Playback" -help = "Enable the default track auto-selection. Enabling this will make the player select streams according to aid, alang, and others. If it is disabled, no tracks are selected. In addition, the player will not exit if no tracks are selected, and wait instead (this wait mode is similar to pausing, but the pause option is not set).\n\nThis is useful with lavfi-complex: you can start playback in this mode, and then set select tracks at runtime by setting the filter graph. Note that if lavfi-complex is set before playback is started, the referenced tracks are always selected." -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "loop-playlist" -file = "mpv" -filter = "Playback" -help = " Loops playback N times. A value of 1 plays it one time (default), 2 two times, etc. inf means forever. no is the same as 1 and disables looping. If several files are specified on command line, the entire playlist is looped. The force mode is like inf, but does not skip playlist entries which have been marked as failing. This means the player might waste CPU time trying to loop a file that doesn't exist. But it might be useful for playing webradios under very bad network conditions." - -[[settings]] -name = "auto-load-folder" -file = "mpvnet" -default = "yes" -filter = "Playback" -help = "For single files automatically load the entire directory into the playlist. Can be suppressed via shift key. (mpv.net specific setting)" -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "input-ar-delay" -file = "mpv" -filter = "Input" -help = "Delay in milliseconds before we start to autorepeat a key (0 to disable)." - -[[settings]] -name = "input-ar-rate" -file = "mpv" -filter = "Input" -help = "Number of key presses to generate per second on autorepeat." - -[[settings]] -name = "update-check" -file = "mpvnet" -default = "no" -filter = "General" -help = "Daily check for new version. (requires PowerShell 5 and curl. mpv.net specific setting)" -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "process-instance" -file = "mpvnet" -default = "single" -filter = "General" -help = "Defines if more then one mpv.net process is allowed. (mpv.net specific setting)\n\nTip: Whenever the control key is pressed when files or URLs are opened, the playlist is not cleared but the files or URLs are appended to the playlist. This not only works on process startup but in all mpv.net features that open files and URLs." -options = [{ name = "multi", help = "Create a new process everytime the shell starts mpv.net" }, - { name = "single", help = "Force a single process everytime the shell starts mpv.net" }, - { name = "queue", help = "Force a single process and add files to playlist" }] - -[[settings]] -name = "recent-count" -file = "mpvnet" -filter = "General" -help = " Amount of recent files to be remembered. Default: 15 (mpv.net specific setting)" - -[[settings]] -name = "video-file-extensions" -file = "mpvnet" -filter = "General" -width = 500 -help = "Video file extensions used to create file associations and used by the auto-load-folder feature. (mpv.net specific setting)" - -[[settings]] -name = "audio-file-extensions" -file = "mpvnet" -filter = "General" -width = 500 -help = "Audio file extensions used to create file associations and used by the auto-load-folder feature. (mpv.net specific setting)" - -[[settings]] -name = "image-file-extensions" -file = "mpvnet" -filter = "General" -width = 500 -help = "Image file extensions used to create file associations and used by the auto-load-folder feature. (mpv.net specific setting)" - -[[settings]] -name = "debug-mode" -file = "mpvnet" -default = "no" -filter = "General" -help = "Enable this only when a developer asks for it. (mpv.net specific setting)" -options = [{ name = "yes" }, - { name = "no" }] - -[[settings]] -name = "dark-mode" -file = "mpvnet" -default = "always" -filter = "UI" -help = "Enables a dark theme. (mpv.net specific setting)" -options = [{ name = "always" }, - { name = "system" , help = "Available on Windows 10 or higher" }, - { name = "never" }] - -[[settings]] -name = "dark-theme" -file = "mpvnet" -filter = "UI" -url = "https://github.com/stax76/mpv.net/blob/master/docs/Manual.md#color-theme" -help = "Color theme used in dark mode. Default: dark" - -[[settings]] -name = "light-theme" -file = "mpvnet" -filter = "UI" -url = "https://github.com/stax76/mpv.net/blob/master/docs/Manual.md#color-theme" -help = "Color theme used in light mode. Default: light" diff --git a/src/Resources/editor_conf.txt b/src/Resources/editor_conf.txt new file mode 100644 index 0000000..b2e676a --- /dev/null +++ b/src/Resources/editor_conf.txt @@ -0,0 +1,656 @@ + +[setting] +name = hwdec +file = mpv +default = no +filter = Video +url = https://mpv.io/manual/master/#options-hwdec +help = Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit: + +option = no always use software decoding +option = auto enable best hw decoder (see below) +option = yes exactly the same as auto +option = auto-copy enable best hw decoder with copy-back (see below) +option = dxva2 requires vo=gpu with gpu-context=d3d11, gpu-context=angle or gpu-context=dxinterop (Windows only) +option = dxva2-copy copies video back to system RAM (Windows only) +option = d3d11va requires vo=gpu with gpu-context=d3d11 or gpu-context=angle (Windows 8+ only) +option = d3d11va-copy copies video back to system RAM (Windows 8+ only) +option = cuda requires vo=gpu (Any platform CUDA is available) +option = cuda-copy copies video back to system RAM (Any platform CUDA is available) +option = nvdec requires vo=gpu (Any platform CUDA is available) +option = nvdec-copy copies video back to system RAM (Any platform CUDA is available) +option = crystalhd copies video back to system RAM (Any platform supported by hardware) +option = rkmpp requires vo=gpu (some RockChip devices only) + +[setting] +name = gpu-api +file = mpv +default = auto +filter = Video +help = Controls which type of graphics APIs will be accepted. Auto uses d3d11, it should only be changed in case of problems, Vulkan is not recommended. + +option = auto Use any available API +option = opengl Allow only OpenGL (requires OpenGL 2.1+ or GLES 2.0+) +option = vulkan Allow only Vulkan (not recommended). +option = d3d11 Allow only gpu-context=d3d11 + +[setting] +name = gpu-context +file = mpv +default = auto +filter = Video + +option = auto auto-select +option = win Win32/WGL +option = winvk VK_KHR_win32_surface +option = angle Direct3D11 through the OpenGL ES translation layer ANGLE. This supports almost everything the win backend does (if the ANGLE build is new enough). +option = dxinterop (experimental) Win32, using WGL for rendering and Direct3D 9Ex for presentation. Works on Nvidia and AMD. Newer Intel chips with the latest drivers may also work. +option = d3d11 Win32, with native Direct3D 11 rendering. + +[setting] +name = vo +file = mpv +default = gpu +filter = Video +help = Video output drivers to be used.\n\nFor more information visit: +url = https://mpv.io/manual/master/#video-output-drivers-vo + +option = gpu General purpose, customizable, GPU-accelerated video output driver. It supports extended scaling methods, dithering, color management, custom shaders, HDR, and more. +option = direct3d Video output driver that uses the Direct3D interface + +[setting] +name = video-sync +file = mpv +default = audio +filter = Video +help = How the player synchronizes audio and video.\n\nFor more information visit: +url = https://mpv.io/manual/master/#options-video-sync + +option = audio +option = display-resample +option = display-resample-vdrop +option = display-resample-desync +option = display-vdrop +option = display-adrop +option = display-desync +option = desync + +[setting] +name = scale +file = mpv +default = bilinear +filter = Video +help = The GPU renderer filter function to use when upscaling video. There are some more filters, but most are not as useful. For a complete list, pass help as value, e.g.: mpv --scale=help + +option = bilinear Bilinear hardware texture filtering (fastest, very low quality). +option = spline36 Mid quality and speed. This is the default when using gpu-hq. +option = lanczos Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc) +option = ewa_lanczos Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc) +option = ewa_lanczossharp A slightly sharpened version of ewa_lanczos, preconfigured to use an ideal radius and parameter. If your hardware can run it, this is probably what you should use by default. +option = mitchell Mitchell-Netravali. The B and C parameters can be set with scale-param1 and scale-param2. This filter is very good at downscaling (see dscale). +option = oversample A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of rounded. This essentially removes the small imperfections and judder artifacts caused by nearest-neighbour interpolation, in exchange for adding some blur. This filter is good at temporal interpolation, and also known as "smoothmotion" (see tscale). + +[setting] +name = cscale +file = mpv +default = bilinear +filter = Video +help = As scale, but for interpolating chroma information. If the image is not subsampled, this option is ignored entirely. + +option = bilinear Bilinear hardware texture filtering (fastest, very low quality). +option = spline36 Mid quality and speed. This is the default when using gpu-hq. +option = lanczos Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc) +option = ewa_lanczos Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc) +option = ewa_lanczossharp A slightly sharpened version of ewa_lanczos, preconfigured to use an ideal radius and parameter. If your hardware can run it, this is probably what you should use by default. +option = mitchell Mitchell-Netravali. The B and C parameters can be set with scale-param1 and scale-param2. This filter is very good at downscaling (see dscale). +option = oversample A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of rounded. This essentially removes the small imperfections and judder artifacts caused by nearest-neighbour interpolation, in exchange for adding some blur. This filter is good at temporal interpolation, and also known as "smoothmotion" (see tscale). + +[setting] +name = dscale +file = mpv +default = auto +filter = Video +help = Like scale, but apply these filters on downscaling instead. + +option = auto Same with the upscaler. +option = bilinear Bilinear hardware texture filtering (fastest, very low quality). +option = spline36 Mid quality and speed. This is the default when using gpu-hq. +option = lanczos Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc) +option = ewa_lanczos Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc) +option = ewa_lanczossharp A slightly sharpened version of ewa_lanczos, preconfigured to use an ideal radius and parameter. If your hardware can run it, this is probably what you should use by default. +option = mitchell Mitchell-Netravali. The B and C parameters can be set with scale-param1 and scale-param2. This filter is very good at downscaling (see dscale). +option = oversample A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of rounded. This essentially removes the small imperfections and judder artifacts caused by nearest-neighbour interpolation, in exchange for adding some blur. This filter is good at temporal interpolation, and also known as "smoothmotion" (see tscale). + +[setting] +name = dither-depth +file = mpv +default = no +filter = Video +help = Set dither target depth to N. Note that the depth of the connected video display device cannot be detected. Often, LCD panels will do dithering on their own, which conflicts with this option and leads to ugly output. + +option = no Disable any dithering done by mpv. +option = auto Automatic selection. If output bit depth cannot be detected, 8 bits per component are assumed. +option = 8 Dither to 8 bit output. +option = 10 Dither to 10 bit output. + +[setting] +name = correct-downscaling +file = mpv +default = no +filter = Video +help = When using convolution based filters, extend the filter size when downscaling. Increases quality, but reduces performance while downscaling.\n\nThis will perform slightly sub-optimally for anamorphic video (but still better than without it) since it will extend the size to match only the milder of the scale factors between the axes. + +option = yes +option = no + +[setting] +name = sigmoid-upscaling +file = mpv +default = no +filter = Video +help = When upscaling, use a sigmoidal color transform to avoid emphasizing ringing artifacts. This also implies linear-scaling. + +option = yes +option = no + +[setting] +name = deband +file = mpv +default = no +filter = Video +help = Enable the debanding algorithm. This greatly reduces the amount of visible banding, blocking and other quantization artifacts, at the expense of very slightly blurring some of the finest details. In practice, it's virtually always an improvement - the only reason to disable it would be for performance. + +option = yes +option = no + +[setting] +name = d3d11va-zero-copy +file = mpv +default = no +filter = Video +help = By default, when using hardware decoding with --gpu-api=d3d11, the video image will be copied (GPU-to-GPU) from the decoder surface to a shader resource. Set this option to avoid that copy by sampling directly from the decoder image. This may increase performance and reduce power usage, but can cause the image to be sampled incorrectly on the bottom and right edges due to padding, and may invoke driver bugs, since Direct3D 11 technically does not allow sampling from a decoder surface (though most drivers support it.) + +option = yes +option = no + +[setting] +name = hdr-compute-peak +file = mpv +default = auto +filter = Video +help = Compute the HDR peak and frame average brightness per-frame instead of relying on tagged metadata. These values are averaged over local regions as well as over several frames to prevent the value from jittering around too much. This option basically gives you dynamic, per-scene tone mapping. Requires compute shaders, which is a fairly recent OpenGL feature, and will probably also perform horribly on some drivers, so enable at your own risk. The special value auto (default) will enable HDR peak computation automatically if compute shaders and SSBOs are supported. + +option = auto +option = yes +option = no + +[setting] +name = volume +file = mpv +filter = Audio +help = Set the startup volume. 0 means silence, 100 means no volume reduction or amplification. Negative values can be passed for compatibility, but are treated as 0. Since mpv 0.18.1, this always controls the internal mixer (aka "softvol"). Default: 100 + +[setting] +name = remember-volume +file = mpvnet +default = yes +filter = Audio +help = Save volume and mute on exit and restore it on start. (mpv.net specific setting) + +option = yes +option = no + +[setting] +name = alang +file = mpv +filter = Audio +type = string +help = Specify a priority list of audio languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two-letter language codes, Matroska, MPEG-TS and NUT use ISO 639-2 three-letter language codes, while OGM uses a free-form identifier. See also aid.\n\nExamples\n\nmpv dvd://1 alang=hu,en chooses the Hungarian language track on a DVD and falls back on English if Hungarian is not available.\n\nmpv alang=jpn example.mkv plays a Matroska file with Japanese audio. + +[setting] +name = audio-file-auto +file = mpv +default = no +filter = Audio +help = Load additional audio files matching the video filename. The parameter specifies how external audio files are matched. + +option = no Don't automatically load external audio files. +option = exact Load the media filename with audio file extension. +option = fuzzy Load all audio files containing media filename. +option = all Load all audio files in the current and audio-file-paths directories. + +[setting] +name = audio-device +file = mpv +filter = Audio +type = string +url = https://mpv.io/manual/master/#options-audio-device +help = Use the given audio device. This consists of the audio output name, e.g. alsa, followed by /, followed by the audio output specific device name. The default value for this option is auto, which tries every audio output in preference order with the default device.\nAvailable devices can be found in the mpv.net context menu under:\nView > Show Audio Devices + +[setting] +name = slang +file = mpv +filter = Subtitle +type = string +help = Specify a priority list of subtitle languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two letter language codes, Matroska uses ISO 639-2 three letter language codes while OGM uses a free-form identifier. See also sid. + +[setting] +name = sub-auto +file = mpv +default = exact +filter = Subtitle +help = Load additional subtitle files matching the video filename. The parameter specifies how external subtitle files are matched. exact is enabled by default. + +option = no Don't automatically load external subtitle files. +option = exact Load the media filename with subtitle file extension. +option = fuzzy Load all subs containing media filename. +option = all Load all subs in the current and sub-file-paths directories. + +[setting] +name = sub-font +file = mpv +filter = Subtitle +type = string +help = Specify font to use for subtitles that do not themselves specify a particular font. The default is sans-serif. + +[setting] +name = sub-font-size +file = mpv +filter = Subtitle +help = Specify the sub font size. The unit is the size in scaled pixels at a window height of 720. The actual pixel size is scaled with the window height: if the window height is larger or smaller than 720, the actual size of the text increases or decreases as well. Default: 55 + +[setting] +name = sub-color +file = mpv +type = color +filter = Subtitle +help = Specify the color used for unstyled text subtitles.\n\nThe color is specified in the form r/g/b, where each color component is specified as number in the range 0.0 to 1.0. It's also possible to specify the transparency by using r/g/b/a, where the alpha value 0 means fully transparent, and 1.0 means opaque. If the alpha component is not given, the color is 100% opaque.\n\nPassing a single number to the option sets the sub to gray, and the form gray/a lets you specify alpha additionally.\n\nExamples\n\n1.0/0.0/0.0 set sub to opaque red\n1.0/0.0/0.0/0.75 set sub to opaque red with 75% alpha\n0.5/0.75 set sub to 50% gray with 75% alpha\n\nAlternatively, the color can be specified as a RGB hex triplet in the form #RRGGBB, where each 2-digit group expresses a color value in the range 0 (00) to 255 (FF). For example, #FF0000 is red. This is similar to web colors. Alpha is given with #AARRGGBB.\n\nExamples\n\n#FF0000 set sub to opaque red\n#C0808080 set sub to 50% gray with 75% alpha + +[setting] +name = sub-border-color +file = mpv +type = color +filter = Subtitle +help = See sub-color. Color used for the sub font border. Ignored when sub-back-color is specified (or more exactly: when that option is not set to completely transparent). + +[setting] +name = sub-back-color +file = mpv +type = color +filter = Subtitle +help = See sub-color. Color used for sub text background. You can use sub-shadow-offset to change its size relative to the text. + +[setting] +name = fullscreen +file = mpv +default = no +filter = Screen +help = Start the player in fullscreen mode. + +option = yes +option = no + +[setting] +name = border +file = mpv +default = yes +filter = Screen +help = Show window with decoration (titlebar, border). + +option = yes +option = no + +[setting] +name = screen +file = mpv +filter = Screen +help = <0-32> In multi-monitor configurations (i.e. a single desktop that spans across multiple displays), this option tells mpv which screen to display the video on. + +[setting] +name = osd-playing-msg +file = mpv +width = 300 +filter = Screen +type = string +help = Show a message on OSD when playback starts. The string is expanded for properties, e.g. osd-playing-msg='file: ${filename}' will show the message file: followed by a space and the currently played filename. For more information visit: +url = https://mpv.io/manual/master/#property-expansion + +[setting] +name = osd-font-size +file = mpv +filter = Screen +help = Specify the OSD font size. See sub-font-size for details. Default: 55 + +[setting] +name = osd-duration +file = mpv +filter = Screen +help = Set the duration of the OSD messages in ms. Default: 1000 + +[setting] +name = osd-scale-by-window +file = mpv +default = yes +filter = Screen +help = Whether to scale the OSD with the window size. If this is disabled, osd-font-size and other OSD options that use scaled pixels are always in actual pixels. The effect is that changing the window size won't change the OSD font size. + +option = yes +option = no + +[setting] +name = autofit +file = mpv +filter = Screen +help = Initial window height in percent. Default: 60 + +[setting] +name = autofit-smaller +file = mpv +filter = Screen +help = Minimum window height in percent. Default: 10 + +[setting] +name = autofit-larger +file = mpv +filter = Screen +help = Maximum window height in percent. Default: 80 + +[setting] +name = start-size +file = mpvnet +default = height-session +filter = Screen +help = Setting to remember the window size. (mpv.net specific setting) + +option = video Window size is set to video resolution +option = width-session Width is remembered in the current session +option = width-always Width is always remembered +option = height-session Height is remembered in the current session +option = height-always Height is always remembered +option = always Size is always remembered + +[setting] +name = start-threshold +file = mpvnet +filter = Screen +help = Threshold in milliseconds to wait for libmpv returning the video resolution before the window is shown, otherwise default dimensions are used as defined by autofit and start-size. Default: 1500 (mpv.net specific setting) + +[setting] +name = minimum-aspect-ratio +file = mpvnet +filter = Screen +help = Minimum aspect ratio, if the AR is smaller than the defined value then the window AR is set to 16/9. This avoids a square window for Music with cover art. Default: 1.2 (mpv.net specific setting) + +[setting] +name = remember-window-position +file = mpvnet +default = no +filter = Screen +help = Save the window position on exit. (mpv.net specific setting) + +option = yes +option = no + +[setting] +name = window-maximized +file = mpv +default = no +filter = Screen +help = Start with a maximized window. + +option = yes +option = no + +[setting] +name = screenshot-directory +file = mpv +width = 500 +type = folder +filter = Screen +help = Store screenshots in this directory. This path is joined with the filename generated by screenshot-template. If the template filename is already absolute, the directory is ignored.\n\nIf the directory does not exist, it is created on the first screenshot. If it is not a directory, an error is generated when trying to write a screenshot. + +[setting] +name = screenshot-format +file = mpv +default = jpg +filter = Screen +help = Set the image file type used for saving screenshots. + +option = jpg +option = png + +[setting] +name = screenshot-tag-colorspace +file = mpv +default = no +filter = Screen +help = Tag screenshots with the appropriate colorspace. Note that not all formats are supported. + +option = yes +option = no + +[setting] +name = screenshot-high-bit-depth +file = mpv +default = yes +filter = Screen +help = If possible, write screenshots with a bit depth similar to the source video. This is interesting in particular for PNG, as this sometimes triggers writing 16 bit PNGs with huge file sizes. This will also include an unused alpha channel in the resulting files if 16 bit is used. + +option = yes +option = no + +[setting] +name = screenshot-jpeg-source-chroma +file = mpv +default = yes +filter = Screen +help = Write JPEG files with the same chroma subsampling as the video. If disabled, the libjpeg default is used. + +option = yes +option = no + +[setting] +name = screenshot-template +file = mpv +filter = Screen +type = string +help = Specify the filename template used to save screenshots. The template specifies the filename without file extension, and can contain format specifiers, which will be substituted when taking a screenshot. By default, the template is mpv-shot%n, which results in filenames like mpv-shot0012.png for example.\n\nFind the full documentation here: +url = https://mpv.io/manual/master/#options-screenshot-template + +[setting] +name = screenshot-jpeg-quality +file = mpv +filter = Screen +help = <0-100> Set the JPEG quality level. Higher means better quality. The default is 90. + +[setting] +name = screenshot-png-compression +file = mpv +filter = Screen +help = <0-9> Set the PNG compression level. Higher means better compression. This will affect the file size of the written screenshot file and the time it takes to write a screenshot. Too high compression might occupy enough CPU time to interrupt playback. The default is 7. + +[setting] +name = screenshot-png-filter +file = mpv +filter = Screen +help = <0-5> Set the filter applied prior to PNG compression. 0 is none, 1 is 'sub', 2 is 'up', 3 is 'average', 4 is 'Paeth', and 5 is 'mixed'. This affects the level of compression that can be achieved. For most images, 'mixed' achieves the best compression ratio, hence it is the default. + +[setting] +name = taskbar-progress +file = mpv +default = yes +filter = Playback +help = Show progress in taskbar. + +option = yes +option = no + +[setting] +name = keep-open-pause +file = mpv +default = yes +filter = Playback +help = If set to no, instead of pausing when keep-open is active, just stop at end of file and continue playing forward when you seek backwards until end where it stops again. + +option = yes +option = no + +[setting] +name = keep-open +file = mpv +default = no +filter = Playback +help = Using no, mpv would terminate after the last file but mpv.net never terminates automatically. + +option = yes If the current file ends, go to the next file, keep the last file open. +option = no If the current file ends, go to the next file. +option = always Playback will never automatically advance to the next file. + +[setting] +name = loop-file +file = mpv +filter = Playback +help = Loop a single file N times. inf means forever, no means normal playback.\n\nThe difference to loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file. loop is an alias for this option. + +[setting] +name = save-position-on-quit +file = mpv +default = no +filter = Playback +help = Always save the current playback position on quit. When this file is played again later, the player will seek to the old playback position on start. This does not happen if playback of a file is stopped in any other way than quitting. For example, going to the next file in the playlist will not save the position, and start playback at beginning the next time the file is played.\n\nThis behavior is disabled by default, but is always available when quitting the player with Shift+Q. + +option = yes +option = no + +[setting] +name = hr-seek +file = mpv +default = absolute +filter = Playback +help = Select when to use precise seeks that are not limited to keyframes. Such seeks require decoding video from the previous keyframe up to the target position and so can take some time depending on decoding performance. For some video formats, precise seeks are disabled. This option selects the default choice to use for seeks; it is possible to explicitly override that default in the definition of key bindings and in input commands. + +option = yes Use precise seeks whenever possible. +option = no Never use precise seeks. +option = absolute Use precise seeks if the seek is to an absolute position in the file, such as a chapter seek, but not for relative seeks like the default behavior of arrow keys. +option = always Same as yes (for compatibility). + +[setting] +name = track-auto-selection +file = mpv +default = yes +filter = Playback +help = Enable the default track auto-selection. Enabling this will make the player select streams according to aid, alang, and others. If it is disabled, no tracks are selected. In addition, the player will not exit if no tracks are selected, and wait instead (this wait mode is similar to pausing, but the pause option is not set).\n\nThis is useful with lavfi-complex: you can start playback in this mode, and then set select tracks at runtime by setting the filter graph. Note that if lavfi-complex is set before playback is started, the referenced tracks are always selected. + +option = yes +option = no + +[setting] +name = loop-playlist +file = mpv +filter = Playback +help = Loops playback N times. A value of 1 plays it one time (default), 2 two times, etc. inf means forever. no is the same as 1 and disables looping. If several files are specified on command line, the entire playlist is looped. The force mode is like inf, but does not skip playlist entries which have been marked as failing. This means the player might waste CPU time trying to loop a file that doesn't exist. But it might be useful for playing webradios under very bad network conditions. + +[setting] +name = auto-load-folder +file = mpvnet +default = yes +filter = Playback +help = For single files automatically load the entire directory into the playlist. Can be suppressed via shift key. (mpv.net specific setting) + +option = yes +option = no + +[setting] +name = input-ar-delay +file = mpv +filter = Input +help = Delay in milliseconds before we start to autorepeat a key (0 to disable). + +[setting] +name = input-ar-rate +file = mpv +filter = Input +help = Number of key presses to generate per second on autorepeat. + +[setting] +name = update-check +file = mpvnet +default = no +filter = General +help = Daily check for new version. (requires PowerShell 5 and curl. mpv.net specific setting) + +option = yes +option = no + +[setting] +name = process-instance +file = mpvnet +default = single +filter = General +help = Defines if more then one mpv.net process is allowed. (mpv.net specific setting)\n\nTip: Whenever the control key is pressed when files or URLs are opened, the playlist is not cleared but the files or URLs are appended to the playlist. This not only works on process startup but in all mpv.net features that open files and URLs. + +option = multi Create a new process everytime the shell starts mpv.net +option = single Force a single process everytime the shell starts mpv.net +option = queue Force a single process and add files to playlist + +[setting] +name = recent-count +file = mpvnet +filter = General +help = Amount of recent files to be remembered. Default: 15 (mpv.net specific setting) + +[setting] +name = video-file-extensions +file = mpvnet +filter = General +width = 500 +help = Video file extensions used to create file associations and used by the auto-load-folder feature. (mpv.net specific setting) + +[setting] +name = audio-file-extensions +file = mpvnet +filter = General +width = 500 +help = Audio file extensions used to create file associations and used by the auto-load-folder feature. (mpv.net specific setting) + +[setting] +name = image-file-extensions +file = mpvnet +filter = General +width = 500 +help = Image file extensions used to create file associations and used by the auto-load-folder feature. (mpv.net specific setting) + +[setting] +name = debug-mode +file = mpvnet +default = no +filter = General +help = Enable this only when a developer asks for it. (mpv.net specific setting) + +option = yes +option = no + +[setting] +name = dark-mode +file = mpvnet +default = always +filter = UI +help = Enables a dark theme. (mpv.net specific setting) + +option = always +option = system Available on Windows 10 or higher +option = never + +[setting] +name = dark-theme +file = mpvnet +filter = UI +url = https://github.com/stax76/mpv.net/blob/master/docs/Manual.md#color-theme +help = Color theme used in dark mode. Default: dark + +[setting] +name = light-theme +file = mpvnet +filter = UI +url = https://github.com/stax76/mpv.net/blob/master/docs/Manual.md#color-theme +help = Color theme used in light mode. Default: light diff --git a/src/WPF/ConfWindow.xaml.cs b/src/WPF/ConfWindow.xaml.cs index 31a1af0..a413528 100644 --- a/src/WPF/ConfWindow.xaml.cs +++ b/src/WPF/ConfWindow.xaml.cs @@ -17,7 +17,7 @@ namespace mpvnet { public partial class ConfWindow : Window { - List SettingsDefinitions = Settings.LoadSettings(Properties.Resources.editor_toml); + List SettingsDefinitions = Conf.LoadConf(Properties.Resources.editor_conf); List ConfItems = new List(); public ObservableCollection FilterStrings { get; } = new ObservableCollection(); string InitialContent; diff --git a/src/DynamicGUI/OptionSettingControl.xaml b/src/WPF/OptionSettingControl.xaml similarity index 98% rename from src/DynamicGUI/OptionSettingControl.xaml rename to src/WPF/OptionSettingControl.xaml index 2a9a74f..c57373f 100644 --- a/src/DynamicGUI/OptionSettingControl.xaml +++ b/src/WPF/OptionSettingControl.xaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:DynamicGUI" + xmlns:local="clr-namespace:mpvnet" mc:Ignorable="d" d:DesignHeight="450" diff --git a/src/DynamicGUI/OptionSettingControl.xaml.cs b/src/WPF/OptionSettingControl.xaml.cs similarity index 100% rename from src/DynamicGUI/OptionSettingControl.xaml.cs rename to src/WPF/OptionSettingControl.xaml.cs diff --git a/src/DynamicGUI/StringSettingControl.xaml b/src/WPF/StringSettingControl.xaml similarity index 98% rename from src/DynamicGUI/StringSettingControl.xaml rename to src/WPF/StringSettingControl.xaml index 41718bf..d5439e9 100644 --- a/src/DynamicGUI/StringSettingControl.xaml +++ b/src/WPF/StringSettingControl.xaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:DynamicGUI" + xmlns:local="clr-namespace:mpvnet" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> diff --git a/src/DynamicGUI/StringSettingControl.xaml.cs b/src/WPF/StringSettingControl.xaml.cs similarity index 100% rename from src/DynamicGUI/StringSettingControl.xaml.cs rename to src/WPF/StringSettingControl.xaml.cs diff --git a/src/mpv.net.csproj b/src/mpv.net.csproj index a18bab3..1c3a03b 100644 --- a/src/mpv.net.csproj +++ b/src/mpv.net.csproj @@ -77,6 +77,7 @@ + @@ -87,6 +88,7 @@ Manual.md + Designer @@ -124,14 +126,13 @@ SearchTextBoxUserControl.xaml - - + + OptionSettingControl.xaml - + StringSettingControl.xaml - @@ -197,7 +198,6 @@ - @@ -208,11 +208,11 @@ - + MSBuild:Compile Designer - + MSBuild:Compile Designer