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); }