public enum DbfVersion : byte { FoxBASE = 0x02, FoxBASENoMemo = 0x03, VisualFoxPro = 0x30, VisualFoxProAutoInc = 0x31, dBASEIVTable = 0x43, dBASEIVSystem = 0x63, FoxBASEMemo = 0x83, dBASEIVMemo = 0x8B, dBASEIVTableMemo = 0xCB, FoxPro2xMemo = 0xF5, FoxBASE2 = 0xFB } [Flags()] public enum FieldFlags : byte { SystemColumn = 0x01, Nullable = 0x02, Binary = 0x04, AutoIncrement = 0x0C } public class Header { public DbfVersion Info; public byte[] LastUpdate; public int NumRecords; public short NumBytesHdr; public short NumBytesRcd; public byte[] Reserved1; public byte IncompleteTrans; public byte Encryption; public byte[] Reserved2; public byte MDXFlg; public byte LangDriverId; public byte[] Reserved3; public List FieldList; public static Header Read(BinaryReader Reader) { Header Result = new Header(); Reader.BaseStream.Seek(0, SeekOrigin.Begin); Result.Info = (DbfVersion)Reader.ReadByte(); Result.LastUpdate = Reader.ReadBytes(3); Result.NumRecords = Reader.ReadInt32(); Result.NumBytesHdr = Reader.ReadInt16(); Result.NumBytesRcd = Reader.ReadInt16(); Result.Reserved1 = Reader.ReadBytes(2); Result.IncompleteTrans = Reader.ReadByte(); Result.Encryption = Reader.ReadByte(); Result.Reserved2 = Reader.ReadBytes(12); Result.MDXFlg = Reader.ReadByte(); Result.LangDriverId = Reader.ReadByte(); Result.Reserved3 = Reader.ReadBytes(2); Result.FieldList = new List(); Reader.BaseStream.Seek(32, SeekOrigin.Begin); while (true) { Field field; if (Reader.PeekChar() == (char)0x0D) { break; } field = Field.Read(Reader); if (field.Type != '0') { Result.FieldList.Add(field); } } return Result; } } public struct Field { public string Name; public char Type; public int Offset; public byte Length; public byte Decimal; public FieldFlags Flags; public int AutoIncrementNext; public byte AutoIncrementStep; public byte[] Reserved; public static Field Read(BinaryReader Reader) { Field field = new Field(); field.Name = new string(Reader.ReadChars(11)); if (field.Name.IndexOf('\0') > 0) { field.Name = field.Name.Substring(0, field.Name.IndexOf('\0')); } field.Type = Reader.ReadChar(); field.Offset = Reader.ReadInt32(); field.Length = Reader.ReadByte(); field.Decimal = Reader.ReadByte(); field.Flags = (FieldFlags)Reader.ReadByte(); field.AutoIncrementNext = Reader.ReadInt32(); field.AutoIncrementStep = Reader.ReadByte(); field.Reserved = Reader.ReadBytes(8); return field; } } public class Row { private DbfFile m_Dbf; private byte[] m_RawData; public bool Deleted; public Dictionary Data; public int Offset; public int RecordNumber; public Row(DbfFile Dbf, int RecordNumber) { m_Dbf = Dbf; Data = new Dictionary(); m_RawData = Dbf.RawReadRecord(RecordNumber); Offset = Dbf.Header.NumBytesHdr + (Dbf.Header.NumBytesRcd * (RecordNumber - 1)); this.RecordNumber = RecordNumber; Deleted = (m_RawData[0] == 0x2A); foreach (Field field in Dbf.Header.FieldList) { Data.Add(field.Name, Translator.ToNET(field.Type, field.Offset, field.Length, field.Decimal, m_RawData)); } } public Row(DbfFile Dbf) { m_Dbf = Dbf; Data = new Dictionary(); Offset = Dbf.Header.NumBytesHdr + (Dbf.Header.NumBytesRcd * (Dbf.Header.NumRecords)); Deleted = false; m_RawData = new byte[Dbf.Header.NumBytesRcd]; m_RawData[0] = 0x20; foreach (Field field in Dbf.Header.FieldList) { Data.Add(field.Name, null); } } public void Write() { byte[] RawData = (byte[])m_RawData.Clone(); RawData[0] = (byte)(Deleted ? 0x2A : 0x20); foreach (Field field in m_Dbf.Header.FieldList) { byte[] fieldData = Translator.ToDBF(field.Type, field.Length, field.Decimal, Data[field.Name], field.Flags == FieldFlags.Nullable); byte[] oldFieldData = new byte[field.Length]; for (int i = 0; i < field.Length; i++) { oldFieldData[i] = RawData[field.Offset + i]; } for (int i = 0; i < field.Length; i++) { if (fieldData[i] != oldFieldData[i]) { System.Diagnostics.Debug.WriteLine(string.Format("[{5,10}, {6}]{0,3}: [(New) 0x{1,2:X} '{2}'] vs. [(Old) 0x{3,2:X} '{4}']", i, fieldData[i], (char)fieldData[i], oldFieldData[i], (char)oldFieldData[i], field.Name, field.Type)); } } fieldData.CopyTo(RawData, field.Offset); } m_Dbf.RawWriteRecord(RecordNumber, RawData); } } public class DbfFile { private Header m_Header; private string m_FileName = ""; private Stream m_FileStream; private BinaryReader m_Reader; private BinaryWriter m_Writer; public Header Header { get { return m_Header; } set { m_Writer.BaseStream.Seek(0, SeekOrigin.Begin); m_Writer.Write((byte)Header.Info); m_Writer.Write(Header.LastUpdate); m_Writer.Write(Header.NumRecords); m_Writer.Write(Header.NumBytesHdr); m_Writer.Write(Header.NumBytesRcd); m_Writer.Write(Header.Reserved1); m_Writer.Write(Header.IncompleteTrans); m_Writer.Write(Header.Encryption); m_Writer.Write(Header.Reserved2); m_Writer.Write(Header.MDXFlg); m_Writer.Write(Header.LangDriverId); m_Writer.Write(Header.Reserved3); m_Header = value; } } public DbfFile(string FileName) { m_FileName = FileName; m_FileStream = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); m_Reader = new BinaryReader(m_FileStream); m_Writer = new BinaryWriter(m_FileStream); m_Header = Header.Read(m_Reader); } ~DbfFile() { m_Reader.Close(); m_Writer.Close(); m_FileStream.Close(); } public byte[] RawReadRecord(int RecordNumber) { m_Reader.BaseStream.Seek(Header.NumBytesHdr + ((RecordNumber - 1) * Header.NumBytesRcd), SeekOrigin.Begin); byte[] Ret = m_Reader.ReadBytes(Header.NumBytesRcd); return Ret; } public void RawWriteRecord(int RecordNumber, byte[] RecordData) { if (RecordData.Length != Header.NumBytesRcd) { throw new Exception("RecordData size does not match size in header"); } if (Header.NumBytesHdr + ((RecordNumber - 1) * Header.NumBytesRcd) > m_Writer.BaseStream.Length) { m_Writer.BaseStream.SetLength(Header.NumBytesHdr + ((RecordNumber - 1) * Header.NumBytesRcd)); Header.NumRecords++; } m_Writer.BaseStream.Seek(Header.NumBytesHdr + ((RecordNumber - 1) * Header.NumBytesRcd), SeekOrigin.Begin); m_Writer.Write(RecordData); } }