internal IEnumerable <IContainer> ToContainers(ModuleSchema schema, ModuleJson module, ModuleAddress parentAddress, string?parentPath, SchemaVariables variables)
        {
            variables = variables.WithVariables(ExtraVariables);
            ModuleOffset offset = ModuleOffset.FromDisplayValue(Offset.Value);

            if (Repeat is null)
            {
                yield return(resolvedContainer.ToContainer(schema, module, ResolvedName, ResolvedDescription,
                                                           parentAddress + offset, parentPath, variables));
            }
            else
            {
                int gap = ModuleOffset.FromDisplayValue(Repeat.Gap.Value).LogicalValue;
                foreach (var tuple in module.GetRepeatSequence(Repeat.Items, variables))
                {
                    var itemVariables        = variables.WithVariable(Repeat.IndexVariable, tuple.index, Repeat.IndexTemplate);
                    var formattedDescription = tuple.variables.Replace(ResolvedDescription);
                    var formattedName        = Invariant($"{ResolvedName}[{tuple.index}]");
                    yield return(resolvedContainer.ToContainer(schema, module, formattedName, formattedDescription,
                                                               parentAddress + offset, parentPath, itemVariables));

                    offset += gap;
                }
            }
        }
Beispiel #2
0
        private IReadOnlyList <FieldBase> ResolveFields(ModuleJson module)
        {
            var resolved = new List <FieldBase>();

            foreach (var field in Fields)
            {
                resolved.AddRange(field.ToFields(module, CurrentSize()));
            }
            var actualSize   = CurrentSize().LogicalValue;
            var expectedSize = ModuleOffset.FromDisplayValue(Size.Value).LogicalValue;

            if (actualSize != expectedSize)
            {
                throw new InvalidOperationException(
                          $"Incorrect size in container {NameInModuleDictionary}: expected {expectedSize}; was {actualSize}");
            }
            // Skip placeholder fields so they're never exposed.
            return(resolved.Where(field => !(field is PlaceholderField)).ToReadOnlyList());

            ModuleOffset CurrentSize()
            {
                var field = resolved.LastOrDefault();

                return(field?.Offset + field?.Size ?? ModuleOffset.Zero);
            }
        }
Beispiel #3
0
        public IEnumerable <IField> ToFields(ModuleJson module, ModuleOffset offset)
        {
            AssertNotNull(ResolvedName);
            AssertNotNull(ResolvedDescription);

            if (Repeat is null)
            {
                yield return(ToField(module, ResolvedName, ResolvedDescription, offset));
            }
            else
            {
                Preconditions.AssertNotNull(Repeat.Items);
                var variables = SchemaVariables.Empty;
                int?gap       = Repeat.Gap?.Value;
                foreach (var tuple in module.GetRepeatSequence(Repeat.Items, variables))
                {
                    var itemVariables = variables.WithVariable(Repeat.IndexVariable, tuple.index);

                    var formattedDescription = tuple.variables.Replace(ResolvedDescription);
                    var formattedName        = Invariant($"{ResolvedName}[{tuple.index}]");
                    var field = ToField(module, formattedName, formattedDescription, offset);
                    yield return(field);

                    offset += gap ?? field.Size;
                }
            }
        }
Beispiel #4
0
 internal FieldBase ToField(ModuleJson module, string name, string description, ModuleOffset offset)
 {
     return(Type switch
     {
         "boolean" => new BooleanField(null, BuildCommon(1), NumericCodec.Range8, GetDefaultValue()),
         "boolean32" => new BooleanField(null, BuildCommon(4), NumericCodec.Range32, GetDefaultValue()),
         string ph when ph.StartsWith("placeholder") => new PlaceholderField(null, BuildCommon(int.Parse(ph.Substring("placeholder".Length)) / 8)),
         "enum" => BuildEnumField(NumericCodec.Range8),
         "enum16" => BuildEnumField(NumericCodec.Range16),
         "enum24" => BuildEnumField(NumericCodec.Full24),
         "enum32" => BuildEnumField(NumericCodec.Range32),
         "instrument" => new InstrumentField(null, BuildCommon(4), BankOffset is null ? (ModuleOffset?)null : ModuleOffset.FromDisplayValue(BankOffset.Value)),
         "midi32" => new NumericField(null, BuildCommon(4), 0, 128, Default ?? 0, NumericCodec.Range32, null, null, null, null, (128, "Off")),
         "overlay" => BuildOverlay(),
         "range8" => BuildNumericField(NumericCodec.Range8),
         "range16" => BuildNumericField(NumericCodec.Range16),
         "urange16" => BuildNumericField(NumericCodec.URange16),
         "range32" => BuildNumericField(NumericCodec.Range32),
         "string" => BuildStringField(1),
         "string16" => BuildStringField(2),
         "tempo" => BuildTempoField(),
         "volume32" => new NumericField(null, BuildCommon(4), -601, 60, 0, NumericCodec.Range32, 10, null, 0, "dB", (-601, "-INF")),
         "fixme_enum32" => BuildEnumField(NumericCodec.Fixme32),
         "fixme_range32" => BuildNumericField(NumericCodec.Fixme32),
         "fixme_boolean32" => new BooleanField(null, BuildCommon(4), NumericCodec.Fixme32, GetDefaultValue()),
         _ => throw new InvalidOperationException($"Invalid field type: '{Type}'")
     });
Beispiel #5
0
        private IReadOnlyList <FieldBase> ResolveFields(ModuleJson module)
        {
            var resolved = new List <FieldBase>();

            foreach (var field in Fields)
            {
                var lastField = resolved.LastOrDefault();
                var offset    = lastField?.Offset + lastField?.Size ?? ModuleOffset.Zero;
                resolved.AddRange(field.ToFields(module, offset));
            }
            // Skip placeholder fields so they're never exposed.
            return(resolved.Where(field => !(field is PlaceholderField)).ToReadOnlyList());
        }
        internal override void ValidateJson(ModuleJson module)
        {
            base.ValidateJson(module);
            // Offset must always be specified
            Validation.ValidateNotNull(Offset, nameof(Offset));
            if (Repeat is object)
            {
                Validation.ValidateNotNull(Repeat.Gap, $"{nameof(Repeat)}.{nameof(Repeat.Gap)}");
            }
            string containerName = Container ?? Name ?? throw new InvalidOperationException($"Either {nameof(Container)} or {nameof(Name)} must be specified");

            Validation.Validate(module.Containers.TryGetValue(containerName, out resolvedContainer),
                                "Container '{0}' not found.", containerName);
        }
Beispiel #7
0
        internal void ValidateAndResolve(ModuleJson module)
        {
            Validation.Validate(Fields is object ^ Containers is object,
                                "Each container must either specify fields or containers as children, but not both.");

            if (Fields is object)
            {
                Validation.Validate(Size is object, "Field containers must specify a size.");
                Fields.ForEach(f => f.ValidateJson(module));
                resolvedFields            = ResolveFields(module);
                requiresOverlayResolution = resolvedFields.Any(f => f is OverlayField overlay && overlay.SwitchPath.Contains('{'));
            }
            else
            {
                Containers?.ForEach(f => f.ValidateJson(module));
            }
        }
        internal INodeDetail ToNodeDetail(ModuleJson module, IContainer nodeContainer, SchemaVariables nodeVariables)
        {
            Validation.Validate(Path is null ^ FormatPaths is null,
                                "Exactly one of Path and FormatPaths must be specified");

            if (Path is object)
            {
                var container = nodeContainer.ResolveContainer(nodeVariables.Replace(Path));
                return(container is FieldContainer fc
                    ? new FieldContainerNodeDetail(Description, fc)
                    : throw new ArgumentException($"'{Path}' does not resolve to a field container"));
            }
            else
            {
                var formattableFields = module.GetRepeatSequence(Repeat, SchemaVariables.Empty)
                                        .ToReadOnlyList(tuple => FieldFormattableString.Create(nodeContainer, Format, FormatPaths, tuple.variables));
                return(new ListNodeDetail(Description, formattableFields));
            }
        }
Beispiel #9
0
 internal FieldBase ToField(ModuleJson module, string name, string description, ModuleOffset offset)
 {
     return(Type switch
     {
         "boolean" => new BooleanField(null, BuildCommon(1)),
         "boolean32" => new BooleanField(null, BuildCommon(4)),
         string ph when ph.StartsWith("placeholder") => new PlaceholderField(null, BuildCommon(int.Parse(ph.Substring("placeholder".Length)) / 8)),
         "enum" => BuildEnumField(1),
         "enum16" => BuildEnumField(2),
         "enum24" => BuildEnumField(3),
         "enum32" => BuildEnumField(4),
         "instrument" => new InstrumentField(null, BuildCommon(4), ModuleOffset.FromDisplayValue(ValidateNotNull(BankOffset, nameof(BankOffset)).Value)),
         "midi32" => new NumericField(null, BuildCommon(4), 0, 128, Default ?? 0, null, null, null, null, (128, "Off")),
         "overlay" => BuildOverlay(),
         "range8" => BuildNumericField(1, 0, 127),
         "range16" => BuildNumericField(2, -128, 127),
         "range32" => BuildNumericField(4, short.MinValue, short.MaxValue),
         "string" => BuildStringField(1),
         "string16" => BuildStringField(2),
         "tempo" => BuildTempoField(),
         "volume32" => new NumericField(null, BuildCommon(4), -601, 60, 0, 10, null, 0, "dB", (-601, "-INF")),
         _ => throw new InvalidOperationException($"Invalid field type: '{Type}'")
     });
Beispiel #10
0
        public IContainer ToContainer(ModuleSchema schema, ModuleJson module, string name, string description, ModuleAddress address, string?parentPath, SchemaVariables variables)
        {
            string path = PathUtilities.AppendPath(parentPath, name);

            if (Fields is object)
            {
                var fieldList = requiresOverlayResolution ? resolvedFields.Select(FinalizeField) : resolvedFields;
                var realSize  = ModuleOffset.FromDisplayValue(Size.Value).LogicalValue;
                return(new FieldContainer(schema, name, description, address, path, realSize, fieldList));

                FieldBase FinalizeField(FieldBase field) =>
                field is OverlayField overlay?overlay.WithPath(variables.Replace(overlay.SwitchPath)) : field;
            }
            else
            {
                var containers = new List <IContainer>();
                foreach (var container in Containers)
                {
                    containers.AddRange(container.ToContainers(schema, module, address, path, variables));
                }
                return(new ContainerContainer(schema, name, description, address, path, containers));
            }
        }
Beispiel #11
0
        internal IEnumerable <TreeNode> ToTreeNodes(ModuleJson json, string?parentNodePath, IContainer parentContainer)
        {
            Preconditions.AssertNotNull(Name);
            Preconditions.AssertNotNull(Path);
            Preconditions.AssertNotNull(Format);
            if (Repeat is null)
            {
                yield return(ToTreeNode(json, Name, parentContainer, SchemaVariables.Empty));
            }
            else
            {
                foreach (var tuple in json.GetRepeatSequence(Repeat, SchemaVariables.Empty))
                {
                    string name = Invariant($"{Name}[{tuple.index}]");
                    yield return(ToTreeNode(json, name, parentContainer, tuple.variables));
                }
            }

            TreeNode ToTreeNode(ModuleJson json, string name, IContainer parentContainer, SchemaVariables variables)
            {
                string nodePath              = PathUtilities.AppendPath(parentNodePath, name);
                string?midiNotePath          = MidiNotePath is null ? null : variables.Replace(MidiNotePath);
                var    resolvedContainerPath = variables.Replace(Path !);
                var    container             = parentContainer.ResolveContainer(resolvedContainerPath);
                var    formattableString     = FieldFormattableString.Create(container, Format !, FormatPaths, variables);

                var childTreeNodes = new List <TreeNode>();

                foreach (var child in Children)
                {
                    childTreeNodes.AddRange(child.ToTreeNodes(json, nodePath, container));
                }
                var treeDetails = Details.Select(detail => detail.ToNodeDetail(json, container, variables)).ToList().AsReadOnly();

                return(new TreeNode(name, nodePath, container, formattableString, midiNotePath, childTreeNodes.AsReadOnly(), treeDetails));
            }
        }
Beispiel #12
0
 private IField ToField(ModuleJson module, string name, string description, ModuleOffset offset)
 {
     return(Type switch
     {
         "boolean" => new BooleanField(BuildCommon(1)),
         "boolean32" => new BooleanField(BuildCommon(4)),
         "placeholder8" => new PlaceholderField(BuildCommon(1)),
         "placeholder16" => new PlaceholderField(BuildCommon(2)),
         "placeholder32" => new PlaceholderField(BuildCommon(4)),
         "enum" => BuildEnumField(1),
         "enum16" => BuildEnumField(2),
         "enum32" => BuildEnumField(4),
         "instrument" => new InstrumentField(BuildCommon(4), ModuleOffset.FromDisplayValue(ValidateNotNull(BankOffset, nameof(BankOffset)).Value)),
         "midi32" => new NumericField(BuildCommon(4), 0, 128, 0, null, null, null, null, (128, "Off")),
         "overlay" => BuildOverlay(),
         "range8" => BuildNumericField(1),
         "range16" => BuildNumericField(2),
         "range32" => BuildNumericField(4),
         "string" => BuildStringField(1),
         "string16" => BuildStringField(2),
         "tempo" => BuildTempoField(),
         "volume32" => new NumericField(BuildCommon(4), -601, 60, 0, 10, null, 0, "dB", (-601, "-INF")),
         _ => throw new InvalidOperationException($"Invalid field type: '{Type}'")
     });
Beispiel #13
0
 internal IField ToFieldForOverlay(ModuleJson module, ModuleOffset offset)
 {
     AssertNotNull(ResolvedName);
     Validate(Repeat is null, "Repeated fields are not valid in overlays");
     return(ToField(module, AssertNotNull(ResolvedName), AssertNotNull(ResolvedDescription), offset));
 }
Beispiel #14
0
 internal virtual void ValidateJson(ModuleJson module)
 {
     ResolvedName        = Validation.ValidateNotNull(Name ?? ToPascalCase(Description), "Name or Description");
     ResolvedDescription = Description ?? Name;
 }