Example #1
0
        private static BinaryHelpFileHeader ReadFileHeader(Stream stream)
        {
            using (BinaryReader reader = new BinaryReader(new StreamView(stream, 0x46, 2)))
            {
                BinaryHelpFileHeader header = new BinaryHelpFileHeader();
                header.Version          = reader.ReadUInt16();
                header.Attributes       = (HelpFileAttributes)reader.ReadUInt16();
                header.ControlCharacter = reader.ReadByte();
                header.Padding1         = reader.ReadByte();
                header.TopicCount       = reader.ReadUInt16();
                header.ContextCount     = reader.ReadUInt16();
                header.DisplayWidth     = reader.ReadByte();
                header.Padding2         = reader.ReadByte();
                header.Padding3         = reader.ReadUInt16();

                byte[] stringData = reader.ReadBytes(14);
                header.DatabaseName = Encoding.ASCII.GetString(stringData);
                int k = header.DatabaseName.IndexOf('\0');
                if (k >= 0)
                {
                    header.DatabaseName = header.DatabaseName.Substring(0, k);
                }

                header.Reserved1            = reader.ReadInt32();
                header.TopicIndexOffset     = reader.ReadInt32();
                header.ContextStringsOffset = reader.ReadInt32();
                header.ContextMapOffset     = reader.ReadInt32();
                header.KeywordsOffset       = reader.ReadInt32();
                header.HuffmanTreeOffset    = reader.ReadInt32();
                header.TopicTextOffset      = reader.ReadInt32();
                header.Reserved2            = reader.ReadInt32();
                header.Reserved3            = reader.ReadInt32();
                header.DatabaseSize         = reader.ReadInt32();

                // TODO: make them warnings instead of errors.
                // COBOL.HLP contains "JCK\r" in Reserved1.
#if false
                // Validate unknown fields.
                if (header.Version != 2)
                {
                    throw new NotSupportedException("Unexpected Version field.");
                }
                if (header.Padding1 != 0 || header.Padding2 != 0 || header.Padding3 != 0)
                {
                    throw new NotSupportedException("Unexpected Padding field.");
                }
                if (header.Reserved1 != 0 || header.Reserved2 != 0 || header.Reserved3 != 0)
                {
                    throw new NotSupportedException("Unexpected Reserved field.");
                }
#endif
                return(header);
            }
        }
Example #2
0
        private static UInt16[] ReadContextMap(BinaryReader reader, BinaryHelpFileHeader header)
        {
            if (reader.BaseStream.Position != header.ContextMapOffset)
            {
                throw new InvalidDataException("Incorrect Context Map section position.");
            }

            UInt16[] contextMap = new UInt16[header.ContextCount];
            for (int i = 0; i < header.ContextCount; i++)
            {
                contextMap[i] = reader.ReadUInt16();
            }
            return(contextMap);
        }
Example #3
0
        private static string[] ReadContextStrings(BinaryReader reader, BinaryHelpFileHeader header)
        {
            if (reader.BaseStream.Position != header.ContextStringsOffset)
            {
                throw new InvalidDataException("Incorrect Context Strings section position.");
            }

            // TODO: the NULL at the very end produces an extra, empty context string.
            // TODO: check exact number of context strings.
            int    size = header.ContextMapOffset - header.ContextStringsOffset;
            string all  = Encoding.ASCII.GetString(reader.ReadBytes(size));

            return(all.Split('\0'));
        }
Example #4
0
        private static int[] ReadTopicIndex(BinaryReader reader, BinaryHelpFileHeader header)
        {
            if (reader.BaseStream.Position != header.TopicIndexOffset)
            {
                throw new InvalidDataException("Incorrect Topic Index section position.");
            }

            int[] topicOffsets = new int[header.TopicCount + 1];
            for (int i = 0; i <= header.TopicCount; i++)
            {
                topicOffsets[i] = reader.ReadInt32();
            }
            return(topicOffsets);
        }
Example #5
0
        private static HuffmanTree ReadHuffmanTree(BinaryReader reader, BinaryHelpFileHeader header)
        {
            if (reader.BaseStream.Position != header.HuffmanTreeOffset)
            {
                throw new InvalidDataException("Incorrect Huffman Tree section position.");
            }

            //int sectionSize = file.Header.TopicTextOffset - file.Header.HuffmanTreeOffset;
            HuffmanTree tree = HuffmanTree.Deserialize(reader);

            if (tree.IsEmpty || tree.IsSingular)
            {
                throw new InvalidDataException("Invalid huffman tree.");
            }
            return(tree);
        }
Example #6
0
        // TODO: this section doesn't terminate by itself?!
        private static byte[][] ReadKeywords(
            BinaryReader reader, BinaryHelpFileHeader header)
        {
            if (reader.BaseStream.Position != header.KeywordsOffset)
            {
                throw new InvalidDataException("Incorrect Keywords section position.");
            }

            int sectionSize = (header.HuffmanTreeOffset > 0 ? header.HuffmanTreeOffset : header.TopicTextOffset)
                              - header.KeywordsOffset;

            byte[] section = reader.ReadBytes(sectionSize);
            if (section.Length != sectionSize)
            {
                throw new InvalidDataException("Cannot fully read dictionary section.");
            }

            return(KeywordListSerializer.Deserialize(section).ToArray());
        }
Example #7
0
        /// <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);
        }