Recently, I came across a situation where I
need to extract the memo field data from an FPT file. After lot of googling I
found this documentation which describes the FPT structure and then I decided to
write my own code to parse the FPT file and extract the data stored in it.
A memo file (FPT) file contains one header and any number of block structures.
The header part is 512 bytes long but the useful information is stored in 7th
and 8th byte which is the size of the block structure.
Following the header is the first memo block. A memo block consists of a memo
block header and memo text. Memo block is 8 bytes long and stores the type of
memo data and the length of the memo data.
A memo block can span over multiple blocks structures but a single block can't
have data of more than one memo block. It means if block is larger than the memo
size then remaining block will be unused.
Here is the code to extract memo data.
public
class MemoBlock
{
public
MemoDataType MemoDataType { get;
private set; }
public Byte[]
Memo { get; private
set; }
public MemoBlock(MemoDataType
type, Byte[] data)
{
this.MemoDataType = type;
this.Memo = data;
}
}
public enum
MemoDataType
{
Picture = 0
, Text = 1
}
public class
MemoCollection :
IEnumerable<MemoBlock>
{
private System.IO.Stream
Stream = null;
private int
blockSize = 0;
public MemoCollection(System.IO.Stream
memoStream)
{
this.Stream = memoStream;
}
#region IEnumerable<MemoBlock>
Members
public IEnumerator<MemoBlock>
GetEnumerator()
{
return GetCollection();
}
#endregion
#region IEnumerable
Members
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private IEnumerator<MemoBlock>
GetCollection()
{
Stream.Seek(0, SeekOrigin.Begin);
GetBlockSize();
while (Stream.Position <
Stream.Length)
{
var memo = ReadMemo();
PositionToNextBlock();
yield
return memo;
}
}
private void
PositionToNextBlock()
{
int unusedbytecount = (int)(Stream.Position
% blockSize);
if (unusedbytecount > 0)
Stream.Seek(blockSize - unusedbytecount,
SeekOrigin.Current);
}
private MemoBlock
ReadMemo()
{
Byte[] header =
new Byte[8];
Stream.Read(header, 0, header.Length);
MemoDataType type = (MemoDataType)ToInteger(header.Take(4).ToArray());
int memoSize =
ToInteger(header.Skip(4).Take(4).ToArray());
Byte[] memoData =
new Byte[memoSize];
Stream.Read(memoData, 0, memoData.Length);
return new
MemoBlock(type, memoData);
}
private void
GetBlockSize()
{
Byte[] buffer =
new Byte[512];
Stream.Read(buffer, 0, buffer.Length);
blockSize = ToInteger(new
Byte[] { 0,0,buffer[6],buffer[7]});
}
private int
ToInteger(byte[] data)
{
if (BitConverter.IsLittleEndian)
Array.Reverse(data);
return
BitConverter.ToInt32(data,0);
}
#endregion
}
To see more from me, u can visit
http://zafarayousafi.blogspot.com/