public HelpTopic DeserializeTopic(byte[] input, SerializationOptions options)
        {
            // Read decompressed length.
            if (input.Length < 2)
            {
                //var e = new InvalidTopicDataEventArgs(topic, input,
                //    "Not enough bytes for DecodedLength field.");
                //this.InvalidTopicData?.Invoke(this, e);
                return(null);
            }
            int outputLength = BitConverter.ToUInt16(input, 0);

            byte[] encodedData = new byte[input.Length - 2];
            Array.Copy(input, 2, encodedData, 0, encodedData.Length);

            // Step 3. Huffman decoding pass.
            byte[] compactData;
            if (options.HuffmanTree != null)
            {
                compactData = HuffmanDecode(encodedData, options.HuffmanTree);
            }
            else
            {
                compactData = encodedData;
            }

            // Step 2. Decompression pass.
            byte[] binaryData = Decompress(compactData, outputLength, options.Keywords);

            // Step 1. Decompile topic.
            HelpTopic topic = DecompileTopic(binaryData, options.ControlCharacter);

            topic.Source = binaryData;

            return(topic);
        }
        /// <summary>
        /// Deserializes the next help database from a binary reader.
        /// </summary>
        /// <remarks>
        /// This method throws an exception if it encounters an irrecoverable
        /// error in the input, such as an IO error or malformed input in the
        /// meta data. It raises an <c>InvalidTopicData</c> event for each
        /// format error it encounters during topic deserialization.
        /// </remarks>
        public HelpDatabase DeserializeDatabase(
            Stream stream, SerializationOptions options)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }
            if (options == null)
            {
                options = new SerializationOptions();
            }

            CheckSignature(stream);

            BinaryHelpFileHeader header  = ReadFileHeader(stream);
            bool         isCaseSensitive = (header.Attributes & HelpFileAttributes.CaseSensitive) != 0;
            HelpDatabase database        = new HelpDatabase(header.DatabaseName, isCaseSensitive);

            options.ControlCharacter = Graphic437.GetChars(new byte[] { header.ControlCharacter })[0];

            using (Stream streamView = new StreamView(stream, header.DatabaseSize, 0x46))
                using (BinaryReader reader = new BinaryReader(streamView))
                {
                    int[] topicOffsets = ReadTopicIndex(reader, header);

                    // Read Context Strings and Context Map sections.
                    if (true)
                    {
                        string[] contextStrings = ReadContextStrings(reader, header);
                        UInt16[] contextMap     = ReadContextMap(reader, header);
                        for (int i = 0; i < header.ContextCount; i++)
                        {
                            database.AddContext(contextStrings[i], contextMap[i]);
                        }
                    }

                    // Read Keywords section.
                    if (header.KeywordsOffset > 0)
                    {
                        options.Keywords     = ReadKeywords(reader, header);
                        options.Compression |= CompressionFlags.Keyword;
                        options.Compression |= CompressionFlags.ExtendedKeyword;
                    }
                    else
                    {
                        options.Keywords = null;
                    }

                    // Read Huffman Tree section.
                    if (header.HuffmanTreeOffset > 0)
                    {
                        options.HuffmanTree = ReadHuffmanTree(reader, header);
                        // file.HuffmanTree.Dump();
                        options.Compression |= CompressionFlags.Huffman;
                    }
                    else
                    {
                        options.HuffmanTree = null;
                    }

                    // Read topic data.
                    if (reader.BaseStream.Position != header.TopicTextOffset)
                    {
                        throw new InvalidDataException("Incorrect topic position.");
                    }
                    for (int i = 0; i < header.TopicCount; i++)
                    {
                        if (reader.BaseStream.Position != topicOffsets[i])
                        {
                            throw new InvalidDataException("Incorrect topic position.");
                        }
                        int inputLength = topicOffsets[i + 1] - topicOffsets[i];

                        byte[]    inputData = reader.ReadBytes(inputLength);
                        HelpTopic topic     = DeserializeTopic(inputData, options);
                        database.Topics.Add(topic);
                    }

                    // TODO: check position
                    if (reader.BaseStream.Position != topicOffsets[header.TopicCount])
                    {
                        throw new InvalidDataException("Incorrect topic end position.");
                    }
                    if (reader.BaseStream.Position != header.DatabaseSize)
                    {
                        throw new InvalidDataException("Incorrect database size.");
                    }
#if DEBUG
                    System.Diagnostics.Debug.WriteLine(string.Format(
                                                           "Decoded database {0} of {1} bytes.",
                                                           database.Name, header.DatabaseSize));
#endif
                }
            return(database);
        }