Пример #1
0
        /// <summary>
        /// Inserts a field at the specified index in the existing list of Structured Fields
        /// </summary>
        /// <param name="newField"></param> The field that will be inserted
        /// <param name="index"></param> The index at which to insert the new field. Will push existing items down from index position and on.
        public void AddField(StructuredField newField, int index)
        {
            // Insert at specified index
            _fields.Insert(index, newField);

            // Sync containers
            SetupContainers(_fields);
        }
Пример #2
0
        /// <summary>
        /// Removes a field from the list of structured fields
        /// </summary>
        /// <param name="field"></param> The field that will be removed from the list.
        public void DeleteField(StructuredField field)
        {
            _fields.Remove(field);

            // Remove this field from all containers that know about it
            foreach (Container c in _fields
                     .Select(f => f.LowestLevelContainer)
                     .Distinct()
                     .Where(cn => cn != null && cn.Structures.Contains(field)))
            {
                c.Structures.Remove(field);
            }

            // Sync containers
            SetupContainers(_fields);
        }
Пример #3
0
        public AFPNode(byte[] data, int start, int end)
        {
            Prefix = data[start];
            Length = data[start + 2] + (data[start + 1] * 256);

            Flags    = data[start + 6];
            Reserved = new byte[] { data[start + 7], data[start + 8] };
            RawData  = new List <byte>();
            for (int i = start + 9; i < end; i++)
            {
                RawData.Add(data[i]);
            }
            //StructuredField sf = new StructuredField((data[start + 3].ToString("x2") + data[start + 4].ToString("x2") + data[start + 5].ToString("x2")).ToUpper());
            StructuredField sf = new StructuredField(data[start + 3], data[start + 4], data[start + 5]);

            Code = sf.Code;
            CategoryDescription = sf.CategoryDescription;
            Name            = sf.Type + ": " + sf.Category;
            CategoryName    = sf.Category;
            TypeDescription = sf.TypeDescription;
            TypeName        = sf.Type;
            //Data
        }
Пример #4
0
        protected List <StructuredField> DecodeStream(byte[] stream, bool parseData)
        {
            List <StructuredField> fields = new List <StructuredField>();

            // Next, loop through each 5A block and store a StructuredField object
            int curIdx = 0;

            while (curIdx < stream.Length - 1)
            {
                byte[] lengthBytes = new byte[2], identifierBytes = new byte[3], introducer = new byte[8], sequenceBytes = new byte[2];
                byte   flag;
                string curOffsetHex = $"0x{curIdx.ToString("X8")}";

                // Check for 0x5A prefix on each field
                if (stream[curIdx] != 0x5A)
                {
                    throw new Exception($"Unexpected byte at offset {curOffsetHex}. Is it a true AFP file?");
                }

                // Check for expected length of data
                if (curIdx + 9 > stream.Length)
                {
                    throw new Exception($"No room for SFI at offset {curOffsetHex}. Is it a true AFP file?");
                }

                // Read the introducer
                Array.ConstrainedCopy(stream, curIdx + 1, introducer, 0, 8);
                Array.ConstrainedCopy(introducer, 0, lengthBytes, 0, 2);
                Array.ConstrainedCopy(introducer, 2, identifierBytes, 0, 3);
                flag = introducer[5];
                Array.ConstrainedCopy(introducer, 6, sequenceBytes, 0, 2);

                // Get a couple pieces of data from introducer
                ushort length   = DataStructure.GetNumericValue <ushort>(lengthBytes);
                ushort sequence = DataStructure.GetNumericValue <ushort>(sequenceBytes);

                // Check the length isn't over what we can read
                if (curIdx + 1 + length > stream.Length)
                {
                    throw new Exception($"Invalid field length of {length} at offset {curOffsetHex}. Is it a true AFP file?");
                }

                // Get the data
                byte[] data = new byte[length - 8];
                Array.ConstrainedCopy(stream, curIdx + 9, data, 0, length - 8);

                // Lookup what type of field we need by the structured fields hex dictionary
                Type   fieldType = typeof(StructuredFields.UNKNOWN);
                string idStr     = BitConverter.ToString(identifierBytes).Replace("-", "");
                if (Lookups.StructuredFields.ContainsKey(idStr))
                {
                    fieldType = Lookups.StructuredFields[idStr];
                }
                StructuredField field = (StructuredField)Activator.CreateInstance(fieldType, identifierBytes, flag, sequence, data);

                // Append to AFP file
                fields.Add(field);

                // Go to next 5A
                curIdx += length + 1;
            }

            SetupContainers(fields);

            // Parse data if needed
            if (parseData)
            {
                foreach (StructuredField field in fields)
                {
                    field.ParseData();
                }
            }

            return(fields);
        }
Пример #5
0
        /// <summary>
        /// Will ensure all Structured Fields in the file are in a place (container) that they should not be, architecturally.
        /// All error messages are added to the list of validation issues
        /// Object and container heirarchy can be found in the MO:DCA documentation
        /// </summary>
        /// <returns>True if all validation passes</returns>
        private bool Validates()
        {
            _messages = new List <string>();

            // Ensure all containers have open/close tags
            foreach (Container c in Fields.Where(f => f.LowestLevelContainer != null).Select(f => f.LowestLevelContainer).Distinct())
            {
                if (c.Structures.Any() && (c.Structures[0].HexID[1] != 0xA8 || c.Structures.Last().HexID[1] != 0xA9))
                {
                    _messages.Add("One or more containers are missing a proper begin and/or end tag.");
                    break;
                }
            }

            // Make sure each begin tag has a container with a matching end tag, and vice versa
            foreach (StructuredField beginOrEnd in Fields.Where(f => f.HexID[1] == 0xA8 || f.HexID[1] == 0xA9))
            {
                if (beginOrEnd.LowestLevelContainer == null || !beginOrEnd.LowestLevelContainer.Structures.Any() ||                       // Container and its structures exist
                    (beginOrEnd.HexID[1] == 0xA8 && beginOrEnd.LowestLevelContainer.Structures[0] != beginOrEnd) ||                       // If begin, first structure is itself
                    (beginOrEnd.HexID[1] == 0xA9 && beginOrEnd.LowestLevelContainer.Structures.Last() != beginOrEnd) || // If end, last structure is itself
                    beginOrEnd.LowestLevelContainer.Structures[0].HexID[2] != beginOrEnd.LowestLevelContainer.Structures.Last().HexID[2]) // Begin/end are same type
                {
                    _messages.Add($"One or more begin/end tags are not enveloped in a proper container.");
                    break;
                }
            }

            // Validate each field's positioning in the defined architecture (skip end tags and NOPs
            foreach (StructuredField field in Fields.Where(f => f.GetType() != typeof(StructuredFields.NOP) && f.HexID[1] != 0xA9))
            {
                Type            fieldType   = field.GetType();
                StructuredField parentField = (StructuredField)(field.HexID[1] == 0xA8 ? field.ParentContainer?.Structures[0] : field.LowestLevelContainer?.Structures[0]);
                Type            parentType  = parentField?.GetType();

                if (parentType == null)
                {
                    // If this is a print file, or a type that is a child of print file, ignore. BPF/EPF tags are not necessary
                    List <Type> subPFTypes = Lookups.FieldsParentOptions.Where(f => f.Value.Contains(typeof(BPF))).Select(f => f.Key).ToList();

                    if (fieldType != typeof(BPF) && !subPFTypes.Contains(fieldType))
                    {
                        _messages.Add($"A {field.Abbreviation} field has no parent container.");
                    }
                }
                else
                {
                    // Verify the existing parent container type (if begin tag), or lowest container type is in the list of accepted parent objects
                    if (Lookups.FieldsParentOptions.ContainsKey(fieldType))
                    {
                        if (!Lookups.FieldsParentOptions[fieldType].Contains(parentType))
                        {
                            _messages.Add($"A {field.Abbreviation} field has an incorrect parent container of type {parentField.Abbreviation}. " +
                                          $"Accepted types are: {string.Join(", ", Lookups.FieldsParentOptions[fieldType].Select(t => t.Name))}.");
                        }
                    }

                    // Disable the "missing lookup" validation - we might not be able to cover every scenario
                    //else
                    //    _validationMessages.Add($"Field type {field.Abbreviation} has no heirarchy information in the lookup table.");
                }
            }

            return(!_messages.Any());
        }