Exemplo n.º 1
0
        void handleException(DataElement elem, BitStream data, Exception e)
        {
            _sizedElements.Remove(elem);
            _sizeRelations.RemoveAll(r => r.Of == elem);

            CrackingFailure ex = e as CrackingFailure;

            if (ex != null)
            {
                logger.Debug("{0} failed to crack.", elem.debugName);
                if (!ex.logged)
                {
                    logger.Debug(ex.Message);
                }
                ex.logged = true;
            }
            else
            {
                logger.Debug("Exception occured: {0}", e.ToString());
            }

            OnExceptionHandleNodeEvent(elem, data.TellBits(), data, e);
        }
Exemplo n.º 2
0
        void addElements(DataElement de, BitStream data, Dictionary <DataElement, Position> positions, long offset)
        {
            Position pos;

            if (!positions.TryGetValue(de, out pos))
            {
                pos = new Position()
                {
                    begin = -offset, end = -offset
                }
            }
            ;

            OnEnterHandleNodeEvent(de, offset + pos.begin, data);

            var cont = de as DataElementContainer;

            if (cont != null)
            {
                foreach (var child in cont)
                {
                    addElements(child, data, positions, offset);
                }
            }

            OnExitHandleNodeEvent(de, offset + pos.end, data);
        }

        #endregion

        #region Handlers

        #region Top Level Handlers

        void handleRoot(DataElement element, BitStream data)
        {
            _sizedElements        = new Dictionary <DataElement, SizedPosition>();
            _sizeRelations        = new List <SizeRelation>();
            _elementsWithAnalyzer = new List <DataElement>();

            // We want at least 1 byte before we begin
            data.WantBytes(1);

            // Crack the model
            handleNode(element, data);

            // Handle any analyzers
            foreach (DataElement elem in _elementsWithAnalyzer)
            {
                OnAnalyzerEvent(elem, data);

                var positions = new Dictionary <DataElement, Position>();
                var parent    = elem.parent;

                try
                {
                    elem.analyzer.asDataElement(elem, positions);
                }
                catch (Exception ex)
                {
                    throw new CrackingFailure("Exception in analyzer on '" + elem.fullName + "': " + ex.Message, elem, data, ex);
                }

                var de  = parent[elem.name];
                var pos = _sizedElements[elem];
                positions[elem] = new Position()
                {
                    begin = 0, end = pos.end - pos.begin
                };
                addElements(de, data, positions, pos.begin);
            }
        }

        /// <summary>
        /// Called to crack a DataElement based on an input stream.  This method
        /// will hand cracking off to a more specific method after performing
        /// some common tasks.
        /// </summary>
        /// <param name="elem">DataElement to crack</param>
        /// <param name="data">Input stream to use for data</param>
        void handleNode(DataElement elem, BitStream data)
        {
            List <BitStream> oldStack = null;

            try
            {
                if (elem == null)
                {
                    throw new ArgumentNullException("elem");
                }
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }

                logger.Debug("------------------------------------");
                logger.Debug("{0} {1}", elem.debugName, data.Progress);

                var pos = handleNodeBegin(elem, data);

                if (elem.transformer != null)
                {
                    long startPos    = data.PositionBits;
                    var  sizedData   = elem.ReadSizedData(data, pos.size);
                    var  decodedData = elem.transformer.decode(sizedData);

                    // Make a new stack of data for the decoded data
                    oldStack   = _dataStack;
                    _dataStack = new List <BitStream>();
                    _dataStack.Add(decodedData);

                    // Use the size of the transformed data as the new size of the element
                    handleCrack(elem, decodedData, decodedData.LengthBits);

                    // Make sure the non-decoded data is at the right place
                    if (data == decodedData)
                    {
                        data.SeekBits(startPos + decodedData.LengthBits, System.IO.SeekOrigin.Begin);
                    }
                }
                else
                {
                    handleCrack(elem, data, pos.size);
                }

                if (elem.constraint != null)
                {
                    handleConstraint(elem, data);
                }

                if (elem.analyzer != null)
                {
                    _elementsWithAnalyzer.Add(elem);
                }

                handleNodeEnd(elem, data, pos);
            }
            catch (Exception e)
            {
                handleException(elem, data, e);
                throw;
            }
            finally
            {
                if (oldStack != null)
                {
                    _dataStack = oldStack;
                }
            }
        }

        void handlePlacelemt(DataElement element, BitStream data)
        {
            var fixups = new List <Tuple <Fixup, string, string> >();
            DataElementContainer oldParent = element.parent;

            // Ensure relations are resolved
            foreach (var relation in element.relations)
            {
                if (relation.Of != element && relation.From != element)
                {
                    throw new CrackingFailure("Error, unable to resolve Relations of/from to match current element.", element, data);
                }
            }

            // Locate relevant fixups
            DataElementContainer root = element.getRoot() as DataElementContainer;

            foreach (DataElement child in root.EnumerateAllElements())
            {
                if (child.fixup == null)
                {
                    continue;
                }

                foreach (var item in child.fixup.references)
                {
                    var refElem = child.find(item.Item2);
                    if (refElem == null)
                    {
                        throw new CrackingFailure("Error, unable to resolve Fixup reference to match current element.", element, data);
                    }

                    if (refElem == element)
                    {
                        fixups.Add(new Tuple <Fixup, string, string>(child.fixup, item.Item1, null));
                    }
                    else if (!refElem.isChildOf(element))
                    {
                        fixups.Add(new Tuple <Fixup, string, string>(child.fixup, item.Item1, refElem.fullName));
                    }
                }
            }

            // Update fixups
            foreach (var fixup in fixups)
            {
                if (fixup.Item3 != null)
                {
                    fixup.Item1.updateRef(fixup.Item2, fixup.Item3);
                }
            }


            string      debugName = element.debugName;
            DataElement newElem   = null;

            if (element.placement.after != null)
            {
                var after = element.find(element.placement.after);
                if (after == null)
                {
                    throw new CrackingFailure("Error, unable to resolve Placement on element '" + element.fullName +
                                              "' with 'after' == '" + element.placement.after + "'.", element, data);
                }
                newElem = element.MoveAfter(after);
            }
            else if (element.placement.before != null)
            {
                DataElement before = element.find(element.placement.before);
                if (before == null)
                {
                    throw new CrackingFailure("Error, unable to resolve Placement on element '" + element.fullName +
                                              "' with 'after' == '" + element.placement.after + "'.", element, data);
                }
                newElem = element.MoveBefore(before);
            }

            // Update fixups
            foreach (var fixup in fixups)
            {
                if (fixup.Item3 == null)
                {
                    fixup.Item1.updateRef(fixup.Item2, newElem.fullName);
                }
            }

            // Clear placement now that it has occured
            newElem.placement = null;

            logger.Debug("handlePlacement: {0} -> {1}", debugName, newElem.fullName);

            OnPlacementEvent(element, newElem, oldParent);
        }

        #endregion

        #region Helpers

        void handleOffsetRelation(DataElement element, BitStream data)
        {
            long?offset = getRelativeOffset(element, data, 0);

            if (!offset.HasValue)
            {
                return;
            }

            offset += data.PositionBits;

            if (offset > data.LengthBits)
            {
                data.WantBytes((offset.Value + 7 - data.LengthBits) / 8);
            }

            if (offset > data.LengthBits)
            {
                string msg = "{0} has offset of {1} bits but buffer only has {2} bits.".Fmt(
                    element.debugName, offset, data.LengthBits);
                throw new CrackingFailure(msg, element, data);
            }

            data.SeekBits(offset.Value, System.IO.SeekOrigin.Begin);
        }

        void handleException(DataElement elem, BitStream data, Exception e)
        {
            _sizedElements.Remove(elem);
            _sizeRelations.RemoveAll(r => r.Of == elem);

            CrackingFailure ex = e as CrackingFailure;

            if (ex != null)
            {
                logger.Debug("{0} failed to crack.", elem.debugName);
                if (!ex.logged)
                {
                    logger.Debug(ex.Message);
                }
                ex.logged = true;
            }
            else
            {
                logger.Debug("Exception occured: {0}", e.ToString());
            }

            OnExceptionHandleNodeEvent(elem, data.PositionBits, data, e);
        }

        void handleConstraint(DataElement element, BitStream data)
        {
            logger.Debug("Running constraint [" + element.constraint + "]");

            Dictionary <string, object> scope = new Dictionary <string, object>();

            scope["element"] = element;

            // Use DefaultValue for constraint, it is the actual cracked value.
            // InternalValue will have relations/fixups applied

            var iv = element.DefaultValue;

            if (iv == null)
            {
                scope["value"] = null;
                logger.Debug("Constraint, value=None.");
            }
            else if (iv.GetVariantType() == Variant.VariantType.ByteString || iv.GetVariantType() == Variant.VariantType.BitStream)
            {
                scope["value"] = (BitwiseStream)iv;
                logger.Debug("Constraint, value=byte array.");
            }
            else
            {
                scope["value"] = (string)iv;
                logger.Debug("Constraint, value=[" + (string)iv + "].");
            }

            object oReturn = Scripting.EvalExpression(element.constraint, scope);

            if (!((bool)oReturn))
            {
                throw new CrackingFailure("Constraint failed.", element, data);
            }
        }

        SizedPosition handleNodeBegin(DataElement elem, BitStream data)
        {
            handleOffsetRelation(elem, data);

            System.Diagnostics.Debug.Assert(!_sizedElements.ContainsKey(elem));

            long?size = getSize(elem, data);

            var pos = new SizedPosition();

            pos.begin = data.PositionBits + getDataOffset();
            pos.size  = size;

            _sizedElements.Add(elem, pos);

            // If this element does not have a size but has a size relation,
            // keep track of the relation for evaluation in the future
            if (!size.HasValue)
            {
                var rel = elem.relations.Of <SizeRelation>();
                _sizeRelations.AddRange(rel);
            }

            OnEnterHandleNodeEvent(elem, pos.begin, data);

            return(pos);
        }

        void handleNodeEnd(DataElement elem, BitStream data, Position pos)
        {
            // Completing this element might allow us to evaluate
            // outstanding size reation computations.

            for (int i = _sizeRelations.Count - 1; i >= 0; --i)
            {
                var rel = _sizeRelations[i];

                if (elem == rel.From)
                {
                    var  other = _sizedElements[rel.Of];
                    long size  = rel.GetValue();

                    if (other.size.HasValue)
                    {
                        logger.Debug("Size relation of {0} cracked again. Updating size from: {1} to: {2}",
                                     rel.Of.debugName, other.size, size);
                    }
                    else
                    {
                        logger.Debug("Size relation of {0} cracked. Updating size to: {1}",
                                     rel.Of.debugName, size);
                    }

                    other.size = size;
                    _sizeRelations.RemoveAt(i);
                }

                var cont = elem as DataElementContainer;
                if (cont != null && cont.isParentOf(rel.From))
                {
                    // If we have finished cracking the parent of the From half of
                    // an outstanding size relation, this means we never cracked
                    // the From element. This can happen when the From half is in
                    // a choice. Just stop tracking the incomplete relation and
                    // keep going.

                    _sizeRelations.RemoveAt(i);
                }
            }

            // Mark the end position of this element
            pos.end = data.PositionBits + getDataOffset();

            OnExitHandleNodeEvent(elem, pos.end, data);
        }

        void handleCrack(DataElement elem, BitStream data, long?size)
        {
            logger.Debug("Crack: {0} Size: {1}, {2}", elem.debugName,
                         size.HasValue ? size.ToString() : "<null>", data.Progress);

            elem.Crack(this, data, size);
        }

        #endregion

        #endregion

        #region Calculate Element Size

        long?getRelativeOffset(DataElement elem, BitStream data, long minOffset = 0)
        {
            var relations = elem.relations.Of <OffsetRelation>();

            if (!relations.Any())
            {
                return(null);
            }

            // Ensure we have cracked the from half of the relation
            var rel = relations.Where(HasCracked).FirstOrDefault();

            if (rel == null)
            {
                return(null);
            }

            // Offset is in bytes
            long offset = (long)rel.GetValue() * 8;

            if (rel.isRelativeOffset)
            {
                DataElement from = rel.From;

                if (rel.relativeTo != null)
                {
                    from = from.find(rel.relativeTo);
                }

                if (from == null)
                {
                    throw new CrackingFailure("Unable to locate 'relativeTo' element in relation attached to " +
                                              elem.debugName + "'.", elem, data);
                }

                // Get the position we are related to
                SizedPosition pos;
                if (!_sizedElements.TryGetValue(from, out pos))
                {
                    return(null);
                }

                // If relativeTo, offset is from beginning of relativeTo element
                // Otherwise, offset is after the From element
                offset += rel.relativeTo != null ? pos.begin : pos.end;
            }

            // Adjust offset to be relative to the current BitStream
            offset -= getDataOffset();

            // Ensure the offset is not before our current position
            if (offset < data.PositionBits)
            {
                string msg = "{0} has offset of {1} bits but already read {2} bits.".Fmt(
                    elem.debugName, offset, data.PositionBits);
                throw new CrackingFailure(msg, elem, data);
            }

            // Make offset relative to current position
            offset -= data.PositionBits;

            // Ensure the offset satisfies the minimum
            if (offset < minOffset)
            {
                string msg = "{0} has offset of {1} bits but must be at least {2} bits.".Fmt(
                    elem.debugName, offset, minOffset);
                throw new CrackingFailure(msg, elem, data);
            }

            return(offset);
        }

        /// <summary>
        /// Searches data for the first occurance of token starting at offset.
        /// </summary>
        /// <param name="data">BitStream to search in.</param>
        /// <param name="token">BitStream to search for.</param>
        /// <param name="offset">How many bits after the current position of data to start searching.</param>
        /// <returns>The location of the token in data from the current position or null.</returns>
        long?findToken(BitStream data, BitwiseStream token, long offset)
        {
            while (true)
            {
                long start = data.PositionBits;
                long end   = data.IndexOf(token, start + offset);

                if (end >= 0)
                {
                    return(end - start);
                }

                long dataLen = data.Length;
                data.WantBytes(token.Length);

                if (dataLen == data.Length)
                {
                    return(null);
                }
            }
        }

        bool?scanArray(Dom.Array array, ref long pos, List <Mark> tokens, Until until)
        {
            logger.Debug("scanArray: {0}", array.debugName);

            int  tokenCount = tokens.Count;
            long arrayPos   = 0;
            var  ret        = scan(array.origionalElement, ref arrayPos, tokens, null, until);

            for (int i = tokenCount; i < tokens.Count; ++i)
            {
                tokens[i].Optional  = array.Count >= array.minOccurs;
                tokens[i].Position += pos;
            }

            if (ret.HasValue && ret.Value)
            {
                if (until == Until.FirstSized)
                {
                    ret = false;
                }

                var relations = array.relations.Of <CountRelation>();
                if (relations.Any())
                {
                    var rel = relations.Where(HasCracked).FirstOrDefault();
                    if (rel != null)
                    {
                        arrayPos *= rel.GetValue();
                        pos      += arrayPos;
                        logger.Debug("scanArray: {0} -> Count Relation: {1}, Size: {2}",
                                     array.debugName, rel.GetValue(), arrayPos);
                        return(ret);
                    }
                    else
                    {
                        logger.Debug("scanArray: {0} -> Count Relation: ???", array.debugName);
                        return(null);
                    }
                }
                else if (array.minOccurs == 1 && array.maxOccurs == 1)
                {
                    arrayPos *= array.occurs;
                    pos      += arrayPos;
                    logger.Debug("scanArray: {0} -> Occurs: {1}, Size: {2}",
                                 array.debugName, array.occurs, arrayPos);
                    return(ret);
                }
                else
                {
                    // If the count is unknown, treat the array unsized
                    ret = null;

                    // If no tokens were found in the array, we are done
                    if (tokenCount == tokens.Count)
                    {
                        logger.Debug("scanArray: {0} -> Count Unknown", array.debugName);
                        return(ret);
                    }
                }
            }

            // If we are looking for the first sized element, try cracking our first element
            if (until == Until.FirstSized)
            {
                logger.Debug("scanArray: {0} -> FirstSized", array.debugName);
                return(false);
            }

            if (tokenCount == tokens.Count)
            {
                logger.Debug("scanArray: {0} -> No Tokens", array.debugName);
                //ret.HasValue ? "Deterministic" : "Unsized");
                return(false);
            }

            // If we have tokens, keep scanning thru the dom.
            logger.Debug("scanArray: {0} -> Tokens", array.debugName);
            return(true);
        }