Extra Systems Database
unit database;

interface

uses
     common, inet, windows, classes, sysutils;

type

     TGetRecordResult = (grrOK, grrError, grrRecordDeleted);

     HTABLE = Integer;

     TESDBField = record
          FieldName      : String[MaxFieldNameLength - 1];
          FieldType      : Integer;
          FieldLength    : Integer;
          FieldPosition  : Integer;
     end;

     TESDBFieldsList = class(TObject)
     private
          FList     : TList;
     public
          constructor Create;
          destructor Destroy; override;
          procedure AddItem(const Field:TESDBField);
          function GetItem(const ItemNumber:Integer; var Field:TESDBField):Boolean;
          function SetItem(const ItemNumber:Integer; const Field:TESDBField):Boolean;
          function Count:Integer;
          procedure Clear;
     end;

     TESDBTablesList = class(TObject)
     private
          FList     : TList;
          procedure AddItem(const Table:TESDBTableItem);
     public
          constructor Create;
          destructor Destroy; override;
          function GetItem(const ItemNumber:Integer; var Table:TESDBTableItem):Boolean;
          function Count:Integer;
     end;

     TESDBDatabase = class(TObject)
     private
          FActive        : Boolean;
          FLockGate      : Integer;
          FSocket        : TSocket;
          procedure Lock;
          procedure Unlock;
          function FindField(const FieldName:String; var Field:TESDBField; const FieldsList:TESDBFieldsList):Boolean;
     public
          constructor Create;
          destructor Destroy; override;
          function CreateDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
          function DropDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
          function Open(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
          procedure Close;
          function CreateTable(const TableName:String; const FieldsList:TESDBFieldsList):Boolean;
          function OpenTable(const TableName:String):HTABLE;
          procedure CloseTable(const hTable:HTABLE);
          function PackTable(const hTable:HTABLE):Boolean;
          procedure GetTableStructure(const hTable:HTABLE; const FieldsList:TESDBFieldsList);
          function PostTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):Boolean;
          function GetTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):TGetRecordResult;
          function DeleteTableRecord(const hTable:HTABLE; const RecordNumber:Integer):Boolean;
          function GetServerInfo(var ServerInfo:TESDBServerInfo):Boolean;
          function GetTotalRecordsCount(const hTable:HTABLE):Integer;
          function GetRealRecordsCount(const hTable:HTABLE):Integer;
          function GetStructure(var TablesList:TESDBTablesList):Boolean;
          function DropTable(const TableName:String):Boolean;
          function LocalBackup(const FileName:String):Boolean;
          function RemoteBackup(const FileName:String):Boolean;
          function SetIntegerField(const FieldName:String; const Value:Integer; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
          function SetStringField(const FieldName:String; const Value:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
          function SetDateTimeField(const FieldName:String; const Value:TDateTime; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
          function GetIntegerField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Integer;
          function GetStringField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):String;
          function GetDateTimeField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):TDateTime;
          property Active:Boolean read FActive;
     end;

implementation

constructor TESDBDatabase.Create;
begin
     inherited Create;
     FLockGate:=0;
     FActive:=False;
     FSocket:=TSocket.Create(864000, 864000);
end;

destructor TESDBDatabase.Destroy;
begin
     Close;
     FSocket.Free;
     inherited Destroy;
end;

procedure TESDBDatabase.Close;
begin
     if not FActive then Exit;
     FSocket.Close;
     FActive:=False;
end;

procedure TESDBDatabase.Lock;
var
     CurVal:Integer;
     i:Integer;
     j:Pointer;
begin
     i:=0;
     j:=@FLockGate;
     while true do begin
          asm
               push eax
               push edx
               mov  eax,1
               mov  edx,j
               xchg eax,[edx]
               mov  CurVal,eax
               pop  edx
               pop  eax
          end;
          if (CurVal = 0) then Break;
          Sleep(i);
          i:=i + 1;
     end;
end;

procedure TESDBDatabase.Unlock;
begin
     FLockGate:=0;
end;

function TESDBDatabase.Open(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
var
     i:Integer;
begin
     Result:=False;
     Close;
     if not FSocket.Connect(@ServerAddress[1], ServerPort) then Exit;
     i:=ESDB_COMMAND_CONNECT_DATABASE;
     FSocket.SendBuffer(@i, 4);
     i:=Length(DatabaseName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@DatabaseName[1], i);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     if Result then begin
          FActive:=True;
     end else begin
          FSocket.Close;
     end;
end;

function TESDBDatabase.CreateDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if FActive then Exit;
     if not FSocket.Connect(@ServerAddress[1], ServerPort) then Exit;
     i:=ESDB_COMMAND_CREATE_DATABASE;
     FSocket.SendBuffer(@i, 4);
     i:=Length(DatabaseName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@DatabaseName[1], i);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     FSocket.Close;
end;

function TESDBDatabase.DropDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if FActive then Exit;
     if not FSocket.Connect(@ServerAddress[1], ServerPort) then Exit;
     i:=ESDB_COMMAND_DROP_DATABASE;
     FSocket.SendBuffer(@i, 4);
     i:=Length(DatabaseName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@DatabaseName[1], i);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     FSocket.Close;
end;

function TESDBDatabase.CreateTable(const TableName:String; const FieldsList:TESDBFieldsList):Boolean;
var
     i,j:Integer;
     FieldDescriptor:TFieldDescriptor;
     Field:TESDBField;
begin
     Result:=False;
     if not FActive then Exit;
     if (FieldsList.Count = 0) then Exit;
     Lock;
     i:=ESDB_COMMAND_CREATE_TABLE;
     FSocket.SendBuffer(@i, 4);
     i:=Length(TableName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@TableName[1], i);
     i:=FieldsList.Count;
     FSocket.SendBuffer(@i, 4);
     for i:=0 to FieldsList.Count - 1 do begin
          FieldsList.GetItem(i, Field);
          FieldDescriptor.FieldType:=Field.FieldType;
          FieldDescriptor.FieldLength:=Field.FieldLength;
          FillChar(FieldDescriptor.FieldName, Sizeof(FieldDescriptor.FieldName), 0);
          j:=Length(Field.FieldName);
          if (j > (Sizeof(FieldDescriptor.FieldName) - 1)) then j:=(Sizeof(FieldDescriptor.FieldName) - 1);
          Move(Field.FieldName[1], FieldDescriptor.FieldName[1], j);
          FSocket.SendBuffer(@FieldDescriptor, Sizeof(FieldDescriptor));
     end;
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     Unlock;
end;

constructor TESDBFieldsList.Create;
begin
     inherited Create;
     FList:=TList.Create;
end;

destructor TESDBFieldsList.Destroy;
var
     i:Integer;
begin
     for i:=0 to FList.Count - 1 do FreeMem(FList[i]);
     FList.Free;
     inherited Destroy;
end;

procedure TESDBFieldsList.AddItem(const Field:TESDBField);
var
     i:Pointer;
begin
     GetMem(i, Sizeof(TESDBField));
     if (i = nil) then Exit;
     Move(Field, i^, Sizeof(TESDBField));
     FList.Add(i);
end;

function TESDBFieldsList.GetItem(const ItemNumber:Integer; var Field:TESDBField):Boolean;
begin
     Result:=False;
     if ((ItemNumber < 0) or (ItemNumber >= FList.Count)) then Exit;
     Result:=True;
     Move(FList[ItemNumber]^, Field, Sizeof(TESDBField));
end;

function TESDBFieldsList.SetItem(const ItemNumber:Integer; const Field:TESDBField):Boolean;
begin
     Result:=False;
     if ((ItemNumber < 0) or (ItemNumber >= FList.Count)) then Exit;
     Result:=True;
     Move(Field, FList[ItemNumber]^, Sizeof(TESDBField));
end;

function TESDBFieldsList.Count:Integer;
begin
     Result:=FList.Count;
end;

procedure TESDBDatabase.CloseTable(const hTable:HTABLE);
var
     i:Integer;
begin
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_CLOSE_TABLE;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     Unlock;
end;

function TESDBDatabase.OpenTable(const TableName:String):HTABLE;
var
     i:Integer;
begin
     Result:=0;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_OPEN_TABLE;
     FSocket.SendBuffer(@i, 4);
     i:=Length(TableName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@TableName[1], i);
     FSocket.ReadToBuffer(@Result, 4);
     Unlock;
end;

procedure TESDBFieldsList.Clear;
begin
     FList.Clear;
end;

procedure TESDBDatabase.GetTableStructure(const hTable:HTABLE; const FieldsList:TESDBFieldsList);
var
     i,j:Integer;
     FieldDescriptor:TFieldDescriptor;
     Field:TESDBField;
begin
     FieldsList.Clear;
     Lock;
     i:=ESDB_QUERY_TABLE_GET_STRUCTURE;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     if (i > 0) then begin
          j:=(i div Sizeof(TFieldDescriptor));
          i:=0;
          while (i < j) do begin
               FSocket.ReadToBuffer(@FieldDescriptor, Sizeof(TFieldDescriptor));
               Field.FieldType:=FieldDescriptor.FieldType;
               Field.FieldLength:=FieldDescriptor.FieldLength;
               Field.FieldName:=StrPas(@FieldDescriptor.FieldName[1]);
               FieldsList.AddItem(Field);
               i:=i + 1;
          end;
     end;
     Unlock;
end;

function TESDBDatabase.GetServerInfo(var ServerInfo:TESDBServerInfo):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     i:=ESDB_QUERY_GETSERVERINFO;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     if (i = Sizeof(TESDBServerInfo)) then begin
          Result:=True;
          FSocket.ReadToBuffer(@ServerInfo, i);
     end;
     Unlock;
end;

function TESDBDatabase.PostTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     if (RecordNumber = 0) then begin
          i:=ESDB_COMMAND_TABLE_APPEND_RECORD;
          FSocket.SendBuffer(@i, 4);
          i:=hTable;
          FSocket.SendBuffer(@i, 4);
     end else begin
          i:=ESDB_COMMAND_TABLE_SET_THIS_RECORD;
          FSocket.SendBuffer(@i, 4);
          i:=hTable;
          FSocket.SendBuffer(@i, 4);
          i:=RecordNumber;
          FSocket.SendBuffer(@i, 4);
     end;
     i:=RecordSize;
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(RecordBuffer, i);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     Unlock;
end;

function TESDBDatabase.DeleteTableRecord(const hTable:HTABLE; const RecordNumber:Integer):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_TABLE_DELETE_THIS_RECORD;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     i:=RecordNumber;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     Unlock;
end;

function TESDBDatabase.GetTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):TGetRecordResult;
var
     i:Integer;
begin
     Result:=grrError;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_TABLE_GET_THIS_RECORD;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     i:=RecordNumber;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     if (i = RecordSize) then begin
          if (FSocket.ReadToBuffer(RecordBuffer, i) = RecordSize) then Result:=grrOK;
     end else begin
          if (i = ESDB_RESULT_ERROR_RECORDDELETED) then Result:=grrRecordDeleted;
     end;
     Unlock;
end;

function TESDBDatabase.GetTotalRecordsCount(const hTable:HTABLE):Integer;
var
     i:Integer;
begin
     Result:=0;
     if not FActive then Exit;
     Lock;
     i:=ESDB_QUERY_TABLE_TOTAL_RECORDS_COUNT;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     if (i > 0) then Result:=i;
     Unlock;
end;

function TESDBDatabase.GetRealRecordsCount(const hTable:HTABLE):Integer;
var
     i:Integer;
begin
     Result:=0;
     if not FActive then Exit;
     Lock;
     i:=ESDB_QUERY_TABLE_REAL_RECORDS_COUNT;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     if (i > 0) then Result:=i;
     Unlock;
end;

constructor TESDBTablesList.Create;
begin
     inherited Create;
     FList:=TList.Create;
end;

destructor TESDBTablesList.Destroy;
var
     i:Integer;
begin
     i:=0;
     while (i < FList.Count) do begin
          FreeMem(FList.Items[i]);
          i:=i + 1;
     end;
     FList.Free;
     inherited Destroy;
end;

procedure TESDBTablesList.AddItem(const Table:TESDBTableItem);
var
     i:Pointer;
begin
     GetMem(i, Sizeof(TESDBTableItem));
     if (i = nil) then Exit;
     Move(Table, i^, Sizeof(TESDBTableItem));
     FList.Add(i);
end;

function TESDBTablesList.GetItem(const ItemNumber:Integer; var Table:TESDBTableItem):Boolean;
begin
     Result:=False;
     if ((ItemNumber < 0) or (ItemNumber >= FList.Count)) then Exit;
     Result:=True;
     Move(FList[ItemNumber]^, Table, Sizeof(TESDBTableItem));
end;

function TESDBTablesList.Count:Integer;
begin
     Result:=FList.Count;
end;

function TESDBDatabase.GetStructure(var TablesList:TESDBTablesList):Boolean;
var
     i,j:Integer;
     TableItem:TESDBTableItem;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_DATABASE_GETSTRUCTURE;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     if (i >= 0) then begin
          Result:=True;
          TablesList.FList.Clear;
          j:=i div Sizeof(TESDBTableItem);
          i:=0;
          while (i < j) do begin
               FSocket.ReadToBuffer(@TableItem, Sizeof(TESDBTableItem));
               TablesList.AddItem(TableItem);
               i:=i + 1;
          end;
     end;
     Unlock;
end;

function TESDBDatabase.DropTable(const TableName:String):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_DROP_TABLE;
     FSocket.SendBuffer(@i, 4);
     i:=Length(TableName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@TableName[1], i);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     Unlock;
end;

function TESDBDatabase.LocalBackup(const FileName:String):Boolean;
var
     i:Integer;
     hFile:Cardinal;
     CurBuf:array[1..512] of Char;
     BufSiz:Integer;
     k:array[1..2] of DWORD;
     j:Pointer;
begin
     Result:=False;
     if not FActive then Exit;
     hFile:=_lcreat(@FileName[1], 0);
     if (hFile = HFILE_ERROR) then Exit;
     Lock;
     i:=ESDB_COMMAND_DATABASE_LOCAL_BACKUP;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@k[1], 8);
     if ((k[1] <> 0) or (k[2] <> 0)) then begin
          Result:=True;
          j:=@k[1];
          while ((k[1] <> 0) or (k[2] <> 0)) do begin
               BufSiz:=FSocket.ReadToBuffer(@CurBuf[1], Sizeof(CurBuf));
               if (BufSiz <= 0) then begin
                    Result:=False;
                    Break;
               end;
               _lwrite(hFile, @CurBuf[1], BufSiz);
               asm
                    push eax
                    push edx
                    push ebx
                    mov  ebx,j
                    mov  eax,[ebx]
                    mov  edx,[ebx + 4]
                    sub  eax,BufSiz
                    sbb  edx,0
                    mov  [ebx],eax
                    mov  [ebx + 4],edx
                    pop  ebx
                    pop  edx
                    pop  eax
               end;
          end;
     end;
     Unlock;
     _lclose(hFile);
     if not Result then DeleteFile(FileName);
end;

function TESDBDatabase.RemoteBackup(const FileName:String):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_DATABASE_REMOTE_BACKUP;
     FSocket.SendBuffer(@i, 4);
     i:=Length(FileName);
     FSocket.SendBuffer(@i, 4);
     FSocket.SendBuffer(@FileName[1], i);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     Unlock;
end;

function TESDBDatabase.PackTable(const hTable:HTABLE):Boolean;
var
     i:Integer;
begin
     Result:=False;
     if not FActive then Exit;
     Lock;
     i:=ESDB_COMMAND_PACK_TABLE;
     FSocket.SendBuffer(@i, 4);
     i:=hTable;
     FSocket.SendBuffer(@i, 4);
     FSocket.ReadToBuffer(@i, 4);
     Result:=(i = ESDB_RESULT_OK);
     Unlock;
end;

function TESDBDatabase.FindField(const FieldName:String; var Field:TESDBField; const FieldsList:TESDBFieldsList):Boolean;
var
     i:Integer;
begin
     i:=0;
     Result:=False;
     while ((i < FieldsList.Count) and (not Result)) do begin
          if FieldsList.GetItem(i, Field) then begin
               Result:=(AnsiUpperCase(FieldName) = AnsiUpperCase(Field.FieldName));
          end;
          i:=i + 1;
     end;
end;

function TESDBDatabase.SetIntegerField(const FieldName:String; const Value:Integer; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
var
     FieldAddress:Pointer;
     Field:TESDBField;
begin
     Result:=False;
     if not FindField(FieldName, Field, FieldsList) then Exit;
     if (Field.FieldType <> FIELD_TYPE_INTEGER) then Exit;
     FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
     Move(Value, FieldAddress^, 4);
     Result:=True;
end;

function TESDBDatabase.SetStringField(const FieldName:String; const Value:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
var
     FieldAddress:Pointer;
     Field:TESDBField;
     i,j:Integer;
begin
     Result:=False;
     if not FindField(FieldName, Field, FieldsList) then Exit;
     if (Field.FieldType <> FIELD_TYPE_CHAR) then Exit;
     FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
     i:=Length(Value);
     j:=Field.FieldLength - 1;
     if (j <= 0) then Exit;
     if (i > j) then i:=j;
     FillChar(FieldAddress^, Field.FieldLength, 0);
     Move(Value[1], FieldAddress^, i);
     Result:=True;
end;

function TESDBDatabase.SetDateTimeField(const FieldName:String; const Value:TDateTime; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
var
     FieldAddress:Pointer;
     Field:TESDBField;
     FieldData:_SYSTEMTIME;
begin
     Result:=False;
     if not FindField(FieldName, Field, FieldsList) then Exit;
     if (Field.FieldType <> FIELD_TYPE_DATE) then Exit;
     DateTimeToSystemTime(Value, FieldData);
     FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
     Move(FieldData, FieldAddress^, Sizeof(_SYSTEMTIME));
     Result:=True;
end;

function TESDBDatabase.GetIntegerField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Integer;
var
     Field:TESDBField;
     FieldAddress:PInteger;
begin
     Result:=0;
     if not FindField(FieldName, Field, FieldsList) then Exit;
     if (Field.FieldType <> FIELD_TYPE_INTEGER) then Exit;
     FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
     Result:=FieldAddress^;
end;

function TESDBDatabase.GetStringField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):String;
var
     Field:TESDBField;
     FieldAddress:PChar;
begin
     Result:='';
     if not FindField(FieldName, Field, FieldsList) then Exit;
     if (Field.FieldType <> FIELD_TYPE_CHAR) then Exit;
     FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
     Result:=StrPas(FieldAddress);
end;

function TESDBDatabase.GetDateTimeField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):TDateTime;
var
     Field:TESDBField;
     FieldAddress:Pointer;
     FieldData:_SYSTEMTIME;
begin
     Result:=0;
     if not FindField(FieldName, Field, FieldsList) then Exit;
     if (Field.FieldType <> FIELD_TYPE_DATE) then Exit;
     FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
     Move(FieldAddress^, FieldData, Sizeof(_SYSTEMTIME));
     Result:=SystemTimeToDateTime(FieldData);
end;

end.