Files
lazarus-ccr/components/tparadoxdataset/paradoxds.pas

819 lines
23 KiB
ObjectPascal
Raw Normal View History

unit paradoxds;
{ TParadoxdataSet
Christian Ulrich christian@ullihome.de
License: LGPL
}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, db, lconvencoding;
const
{ Paradox codes for field types }
pxfAlpha = $01;
pxfDate = $02;
pxfShort = $03;
pxfLong = $04;
pxfCurrency = $05;
pxfNumber = $06;
pxfLogical = $09;
pxfMemoBLOb = $0C;
pxfBLOb = $0D;
pxfFmtMemoBLOb = $0E;
pxfOLE = $0F;
pxfGraphic = $10;
pxfTime = $14;
pxfTimestamp = $15;
pxfAutoInc = $16;
pxfBCD = $17;
pxfBytes = $18;
type
{Internal Record information}
PRecInfo = ^TRecInfo;
TRecInfo = packed record
RecordNumber: PtrInt;
BookmarkFlag: TBookmarkFlag;
end;
PLongWord = ^Longword;
{ field information record used in TPxHeader below }
PFldInfoRec = ^TFldInfoRec;
TFldInfoRec = packed record
fType: byte;
fSize: byte;
end;
PPxHeader = ^TPxHeader;
TPxHeader = packed record
recordSize : word;
headerSize : word;
fileType : byte;
maxTableSize : byte;
numRecords : longint;
nextBlock : word;
fileBlocks : word;
firstBlock : word;
lastBlock : word;
unknown12x13 : word;
modifiedFlags1 : byte;
indexFieldNumber : byte;
primaryIndexWorkspace : longint; // currently not used; cast to "pointer"
unknownPtr1A : longint; // not used; cast to pointer;
unknown1Ex20 : array[$001E..$0020] of byte;
numFields : smallint;
primaryKeyFields : smallint;
encryption1 : longint;
sortOrder : byte;
modifiedFlags2 : byte;
unknown2Bx2C : array[$002B..$002C] of byte;
changeCount1 : byte;
changeCount2 : byte;
unknown2F : byte;
tableNamePtrPtr : longint; // must be cast to ^pchar
fldInfo : longint; // use FFieldInfoPtr instead
writeProtected : byte;
fileVersionID : byte;
maxBlocks : word;
unknown3C : byte;
auxPasswords : byte;
unknown3Ex3F : array[$003E..$003F] of byte;
cryptInfoStartPtr : longint; // not used; cast to pointer
cryptInfoEndPtr : longint; // not used; cast to pointer
unknown48 : byte;
autoIncVal : longint;
unknown4Dx4E : array[$004D..$004E] of byte;
indexUpdateRequired : byte;
unknown50x54 : array[$0050..$0054] of byte;
refIntegrity : byte;
unknown56x57 : array[$0056..$0057] of byte;
case smallint of
3: (fieldInfo35 : array[1..255] of TFldInfoRec);
4: (fileVerID2 : smallint;
fileVerID3 : smallint;
encryption2 : longint;
fileUpdateTime : longint; { 4.0 only }
hiFieldID : word;
hiFieldIDinfo : word;
sometimesNumFields:smallint;
dosCodePage : word;
unknown6Cx6F : array[$006C..$006F] of byte;
changeCount4 : smallint;
unknown72x77 : array[$0072..$0077] of byte;
fieldInfo : array[1..255] of TFldInfoRec);
{ This is only the first part of the file header. The last field
is described as an array of 255 elements, but its size is really
determined by the number of fields in the table. The actual
table header has more information that follows. }
end;
{Paradox Data Block Header}
PDataBlock = ^TDataBlock;
TDataBlock = packed RECORD
nextBlock : word;
prevBlock : word;
addDataSize : smallint;
//fileData : array[0..$0FF9] of byte;
{ fileData size varies according to maxTableSize }
end;
TPxField = record
Info: PFldInfoRec;
Offset: LongInt;
Name: String;
end;
{10-byte Blob Info Block}
TPxBlobInfo = packed record
FileLoc: LongWord;
Length: LongWord;
ModCount: Word;
end;
{Blob Pointer Array Entry}
TPxBlobIndex = packed record
Offset: Byte;
Len16: Byte;
ModCount: Word;
Len: Byte;
end;
{ TParadoxDataset }
TParadoxDataset = class(TDataset)
private
FActive: Boolean;
FStream: TStream;
FBlobStream: TStream;
FFileName: TFileName;
FHeader: PPxHeader;
FaRecord: LongInt; // was: LongWord;
FaBlockstart: LongInt;
FaBlock: PDataBlock;
FaBlockIdx: word;
FBlockReaded: Boolean;
FFieldInfoPtr: PFldInfoRec;
FTableNameLen: Integer;
FInputEncoding: String;
FTargetEncoding: String;
FPxFields: Array of TPxField;
function GetEncrypted: Boolean;
function GetInputEncoding: String; inline;
function GetTargetEncoding: String; inline;
function GetVersion: String;
function IsStoredTargetEncoding: Boolean;
procedure ReadBlock;
procedure ReadNextBlockHeader;
procedure ReadPrevBlockHeader;
procedure SetFileName(const AValue: TFileName);
procedure SetTargetEncoding(AValue: String);
protected
function AllocRecordBuffer: PChar; override;
procedure FreeRecordBuffer(var Buffer: PChar); override;
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
function GetCanModify: Boolean;override;
function GetRecNo: Integer; override;
function GetRecord(Buffer: PChar; GetMode: TGetMode; {%H-}DoCheck: Boolean): TGetResult; override;
function GetRecordCount: Integer; override;
function GetRecordSize: Word; override;
procedure InternalClose; override;
procedure InternalEdit; override;
procedure InternalFirst; override;
procedure InternalGotoBookmark(ABookmark: Pointer); override;
procedure InternalHandleException; override;
procedure InternalInitFieldDefs; override;
procedure InternalInitRecord(Buffer: PChar); override;
procedure InternalLast; override;
procedure InternalOpen; override;
procedure InternalPost; override;
procedure InternalSetToRecord(Buffer: PChar); override;
function IsCursorOpen: Boolean; override;
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
procedure SetRecNo(Value: Integer); override;
public
constructor Create(AOwner: TComponent); override;
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
procedure SetFieldData({%H-}Field: TField; {%H-}Buffer: Pointer); override;
property Encrypted: Boolean read GetEncrypted;
published
property TableName: TFileName read FFileName write SetFileName;
property TableLevel: String read GetVersion;
property InputEncoding: String read FInputEncoding;
property TargetEncoding: String read FTargetEncoding write SetTargetEncoding stored IsStoredTargetEncoding;
property Active;
property AutoCalcFields;
property FieldDefs;
property Filtered;
property BeforeOpen;
property AfterOpen;
property BeforeClose;
property AfterClose;
// property BeforeInsert;
// property AfterInsert;
// property BeforeEdit;
// property AfterEdit;
// property BeforePost;
// property AfterPost;
// property BeforeCancel;
// property AfterCancel;
// property BeforeDelete;
// property AfterDelete;
property BeforeScroll;
property AfterScroll;
// property BeforeRefresh;
// property AfterRefresh;
property OnCalcFields;
// property OnDeleteError;
// property OnEditError;
property OnFilterRecord;
// property OnNewRecord;
// property OnPostError;
end;
implementation
uses
Forms;
{ TParadoxDataset }
constructor TParadoxDataset.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHeader := nil;
FTargetEncoding := Uppercase(EncodingUTF8);
FInputEncoding := '';
end;
function TParadoxDataset.AllocRecordBuffer: PChar;
begin
if Assigned(Fheader) then
Result := AllocMem(GetRecordSize)
else
Result := nil;
end;
function TParadoxDataset.CreateBlobStream(Field: TField;
Mode: TBlobStreamMode): TStream;
var
memStream: TMemoryStream;
p: PChar;
header: PAnsiChar;
idx: Byte;
loc: Integer;
s: String;
blobInfo: TPxBlobInfo;
blobIndex: TPxBlobIndex;
begin
memStream := TMemoryStream.Create;
Result := memStream;
if (Mode <> bmRead) then
exit;
p := ActiveBuffer + FPxFields[Field.FieldNo - 1].Offset;
header := p + Field.Size - SizeOf(TPxBlobInfo);
Move(header^, blobInfo{%H-}, SizeOf(blobInfo));
if blobInfo.Length = 0 then
exit;
if Integer(blobInfo.Length) > Field.Size - SizeOf(TPxBlobInfo) then
begin
if Assigned(FBlobStream) then begin
idx := blobInfo.FileLoc and $FF;
loc := blobInfo.FileLoc and $FFFFFF00;
if idx = $FF then begin
// Read from a single blob block
FBlobStream.Seek(loc + 9, soFromBeginning);
if Field.DataType = ftMemo then begin
SetLength(s, blobInfo.Length);
FBlobStream.Read(s[1], blobInfo.Length);
s := ConvertEncoding(s, GetInputEncoding, GetTargetEncoding);
memStream.Write(s[1], Length(s));
end else
begin
if Field.DataType = ftGraphic then begin
memstream.WriteAnsiString('bmp'); // Assuming that Paradox can store only bmp as ftGraphic... Wrong?
FBlobStream.Position := FBlobStream.Position + 8;
end;
memStream.CopyFrom(FBlobStream, blobInfo.Length);
end;
end else begin
// Read from a suballocated block
FBlobStream.Seek(loc + 12 + 5*idx, soFromBeginning);
FBlobStream.Read(blobIndex{%H-}, SizeOf(TPxBlobIndex));
FBlobStream.Seek(loc + 16*blobIndex.Offset, soFromBeginning);
if Field.DataType = ftMemo then begin
SetLength(s, blobInfo.Length);
FBlobStream.Read(s[1], blobInfo.Length);
s := ConvertEncoding(s, GetInputEncoding, GetTargetEncoding);
memStream.Write(s[1], Length(s));
end else
memStream.CopyFrom(FBlobStream, blobInfo.Length);
end;
end;
end else
if Field.DataType = ftMemo then begin
SetLength(s, blobInfo.Length);
Move(p^, s[1], blobInfo.Length);
s := ConvertEncoding(s, GetInputEncoding, GetTargetEncoding);
memStream.Write(s[1], Length(s));
end else
memStream.Write(p, blobInfo.Length);
memStream.Position := 0;
end;
procedure TParadoxDataset.FreeRecordBuffer(var Buffer: PChar);
begin
if Assigned(Buffer) then
FreeMem(Buffer);
end;
procedure TParadoxDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
begin
//TODO
end;
function TParadoxDataset.GetCanModify: Boolean;
begin
Result := False;
end;
function TParadoxDataset.GetEncrypted: Boolean;
begin
if not Assigned(FHeader) then exit;
If (FHeader^.fileVersionID <= 4) or not (FHeader^.fileType in [0,2,3,5]) then
Result := (FHeader^.encryption1 <> 0)
else
Result := (FHeader^.encryption2 <> 0)
end;
function TParadoxDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
var
b: WordBool;
F: PFldInfoRec;
i: Integer;
size: Integer;
p: PChar;
s: array[0..7] of byte;
si: SmallInt absolute s;
int: LongInt absolute s;
d: Double absolute s;
str: String;
begin
Result := False;
if (RecordCount = 0) then
exit;
F := FPxFields[Field.FieldNo - 1].Info;
p := ActiveBuffer + FPxFields[Field.FieldNo - 1].Offset;
size := F^.fSize;
// These numeric fields are stored as big endian --> swap bytes
if F^.fType in [pxfDate..pxfNumber, pxfTime..pxfAutoInc] then begin
for i := 0 to pred(size) do
s[pred(size-i)] := byte(p[i]);
s[pred(size)] := s[pred(size)] xor $80;
end;
case F^.fType of
pxfAlpha:
if (Buffer <> nil) then begin
str := ConvertEncoding(StrPas(p), GetInputEncoding, GetTargetEncoding);
if str <> '' then begin
StrLCopy(Buffer, PChar(str), Length(str));
Result := true;
end;
end;
pxfBytes:
if Buffer <> nil then begin
StrLCopy(Buffer, PAnsiChar(p), F^.fSize);
Result := true;
end;
pxfDate:
if int <> $FFFFFFFF80000000 then begin // This transforms to Dec/12/9999 and probably is NULL
Move(int, Buffer^, SizeOf(LongInt));
Result := True;
end;
pxfShort:
begin
Move(si, Buffer^, SizeOf(SmallInt));
Result := True;
end;
pxfLong, pxfAutoInc:
begin
Move(int, Buffer^, SizeOf(LongInt));
Result := True;
end;
pxfCurrency, pxfNumber, pxfTimeStamp:
begin
Move(d, Buffer^, SizeOf(d));
Result := True;
end;
pxfLogical:
begin
b := not ((p^ = #$80) or (p^ = #0));
if Assigned(Buffer) then
Move(b, Buffer^, SizeOf(b));
Result := true; // Keep outside "if Assigned" otherwise checkboxes will be wrong.
end;
pxfTime:
begin
Move(int, Buffer^, SizeOf(LongInt));
Result := True;
end;
pxfGraphic:
Result := ActiveBuffer <> nil;
end;
end;
function TParadoxDataset.GetInputEncoding: String;
begin
if FInputEncoding = '' then
Result := GetDefaultTextEncoding
else
Result := FInputEncoding;
end;
function TParadoxDataset.GetRecNo: Integer;
begin
Result := -1;
if Assigned(ActiveBuffer) then
Result := PRecInfo(ActiveBuffer + FHeader^.recordSize)^.RecordNumber;
end;
function TParadoxDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
var
L: Longword;
begin
Result := grOK;
case GetMode of
gmNext:
begin
inc(FaRecord);
if (FaBlockIdx = FHeader^.lastBlock) and
(FaRecord > FaBlockStart + FaBlock^.addDataSize div FHeader^.recordSize + 1)
then
Result := grEOF
else
begin
if FaRecord > FaBlockStart+1+(FaBlock^.addDataSize div FHeader^.recordSize) then
ReadNextBlockHeader;
end;
end;
gmPrior:
begin
dec(FaRecord);
if (FaBlockIdx = FHeader^.firstBlock) and (FaRecord < 1) then
Result := grBOF
else
begin
if FaRecord <= FaBlockStart then
begin
ReadPrevBlockHeader;
FaRecord := FaBlockStart+(FaBlock^.addDataSize div FHeader^.recordSize)+1;
end;
end;
end;
gmCurrent:
begin
if (FaRecord > RecordCount) or (FaRecord < 1) then
result := grError;
end;
end;
if Result = grOK then
begin
if not FBlockreaded then
ReadBlock;
L := ((faRecord-(FaBlockstart+1))*FHeader^.recordSize)+6;
if (faRecord-(FaBlockstart+1)) >= 0 then
begin
Move(PChar(FaBlock)[L],Buffer[0],FHeader^.recordSize);
end
else
result := grError;
with PRecInfo(Buffer + FHeader^.recordSize)^ do
begin
BookmarkFlag := bfCurrent;
RecordNumber := FaRecord;
end;
end;
end;
function TParadoxDataset.GetRecordCount: Integer;
begin
if Assigned(FHeader) then
Result := FHeader^.numRecords
else
Result := 0;
end;
function TParadoxDataset.GetRecordSize: Word;
begin
Result := FHeader^.recordSize + sizeof(TRecInfo);
end;
function TParadoxDataset.GetTargetEncoding: String;
begin
if (FTargetEncoding = '') or SameText(FTargetEncoding, 'utf-8') then
Result := EncodingUTF8
else
Result := FTargetEncoding;
end;
function TParadoxDataset.GetVersion: String;
begin
Result := '';
if not FActive then
exit;
if not Assigned(FHeader) then
exit;
case FHeader^.fileVersionID of
$3 : Result := '3.0';
$4 : Result := '3.5';
$5..$9 : Result := '4.0';
$a..$b : Result := '5.0';
$c : Result := '7.0';
end;
end;
procedure TParadoxDataset.InternalClose;
begin
BindFields(FALSE);
if DefaultFields then // Destroy the TField
DestroyFields;
FreeMem(FHeader);
FreeMem(FaBlock);
FreeAndNil(FBlobStream);
FreeAndNil(FStream);
FActive := False;
end;
procedure TParadoxDataset.InternalEdit;
begin
end;
procedure TParadoxDataset.InternalFirst;
begin
FaBlockIdx := FHeader^.firstBlock;
FaBlockstart := 0;
FaRecord := 0;
ReadBlock;
end;
procedure TParadoxDataset.InternalGotoBookmark(ABookmark: Pointer);
begin
SetRecNo(PLongWord(ABookmark)^);
end;
procedure TParadoxDataset.InternalHandleException;
begin
Application.HandleException(Self);
end;
procedure TParadoxDataset.InternalInitFieldDefs;
var
i: integer;
F: PFldInfoRec;
FNamesStart: PChar;
fname: String;
offs: LongInt;
begin
FieldDefs.Clear;
F := FFieldInfoPtr; { begin with the first field identifier }
FNamesStart := Pointer(F);
inc(FNamesStart, SizeOf(F^)*(FHeader^.numFields)); //Jump over Fielddefs
inc(FNamesStart, SizeOf(LongInt)); //over TableName pointer
inc(FNamesStart, SizeOf(LongInt)*(FHeader^.numFields)); //over FieldName pointers
inc(FNamesStart, FTableNameLen); // over Tablename and padding
SetLength(FPxFields, FHeader^.NumFields);
offs := 0;
for i := 1 to FHeader^.NumFields do
begin
fname := ConvertEncoding(StrPas(FNamesStart), GetInputEncoding, GetTargetEncoding);
case F^.fType of
pxfAlpha: FieldDefs.Add(fname, ftString, F^.fSize);
pxfDate: FieldDefs.Add(fname, ftDate, 0);
pxfShort: FieldDefs.Add(fname, ftSmallInt, F^.fSize);
pxfLong: FieldDefs.Add(fname, ftInteger, F^.fSize);
pxfCurrency: FieldDefs.Add(fname, ftCurrency, F^.fSize);
pxfNumber: FieldDefs.Add(fname, ftFloat, F^.fSize);
pxfLogical: FieldDefs.Add(fname, ftBoolean, 0); //F^.fSize);
pxfMemoBLOb: FieldDefs.Add(fname, ftMemo, F^.fSize);
pxfBLOb: FieldDefs.Add(fname, ftBlob, F^.fSize);
pxfFmtMemoBLOb: FieldDefs.Add(fname, ftMemo, F^.fSize);
pxfOLE: FieldDefs.Add(fname, ftBlob, F^.fSize);
pxfGraphic: FieldDefs.Add(fname, ftGraphic, F^.fSize); // was: ftBlob
pxfTime: FieldDefs.Add(fname, ftTime, 0); //F^.fSize);
pxfTimestamp: FieldDefs.Add(fname, ftDateTime, 0);
pxfAutoInc: FieldDefs.Add(fname, ftAutoInc, F^.fSize);
pxfBCD: FieldDefs.Add(fname, ftBCD, F^.fSize);
pxfBytes: FieldDefs.Add(fname, ftBytes, F^.fSize); // was: ftString
end;
with FPxFields[i-1] do begin
Name := fname;
Info := F;
Offset := offs;
end;
offs := offs + F^.fSize;
inc(FNamesStart, Length(fname)+1);
inc(F);
end;
end;
procedure TParadoxDataset.InternalInitRecord(Buffer: PChar);
begin
end;
procedure TParadoxDataset.InternalLast;
begin
while FaBlockIdx <> FHeader^.lastBlock do
ReadNextBlockHeader;
inc(FaRecord,(FaBlock^.addDataSize div FHeader^.RecordSize)+1);
end;
procedure TParadoxDataset.InternalOpen;
var
hdrSize: Word;
blobfn: String;
cp: Word;
begin
if FFileName = '' then
DatabaseError('Tablename is not set');
if not FileExists(FFileName) then
DatabaseError(Format('Paradox file "%" does not exist.', [FFileName]));
FStream := TFileStream.Create(FFilename,fmOpenRead or fmShareDenyNone);
FStream.Position := 2;
hdrSize := FStream.ReadWord;
FHeader := AllocMem(hdrSize);
FStream.Position := 0;
if not FStream.Read(FHeader^, hdrSize) = hdrSize then
DatabaseError('No valid Paradox file !');
if not ((FHeader^.maxTableSize >= 1) and (FHeader^.maxTableSize <= 32)) then
DatabaseError('No valid Paradox file !');
if (FHeader^.fileVersionID >= 12) then
FTableNameLen := 261
else
FTableNameLen := 79;
if (FHeader^.fileVersionID <= 4) or not (FHeader^.FileType in [0,2,3,5]) then
FFieldInfoPtr := @FHeader^.FieldInfo35
else begin
FFieldInfoPtr := @FHeader^.FieldInfo;
cp := FHeader^.DosCodePage;
FInputEncoding := 'cp' + IntToStr(cp);
end;
if Encrypted then
exit;
FBlobStream := nil;
blobfn := ChangeFileExt(FFileName, '.mb');
if FileExists(blobfn) then
FBlobStream := TFileStream.Create(blobfn, fmOpenRead + fmShareDenyNone)
else begin
blobfn := ChangeFileExt(FFileName, '.MB');
if FileExists(blobfn) then
FBlobStream := TFileStream.Create(blobfn, fmOpenRead + fmShareDenyNone);
end;
FaBlock := AllocMem(FHeader^.maxTableSize * $0400);
BookmarkSize := SizeOf(longword);
InternalFirst;
InternalInitFieldDefs;
if DefaultFields then CreateFields;
BindFields(True);
FActive := True;
end;
procedure TParadoxDataset.InternalPost;
begin
end;
procedure TParadoxDataset.InternalSetToRecord(Buffer: PChar);
begin
if (State <> dsInsert) then
InternalGotoBookmark(@PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber);
end;
function TParadoxDataset.IsCursorOpen: Boolean;
begin
Result := FActive;
end;
function TParadoxDataset.IsStoredTargetEncoding: Boolean;
begin
Result := not SameText(FTargetEncoding, EncodingUTF8);
end;
procedure TParadoxDataset.ReadBlock;
var
L : longint;
begin
L := FaBlockIdx-1;
L := (L * FHeader^.maxTableSize * $0400) + FHeader^.headerSize;
FStream.Position := L;
FStream.Read(FaBlock^, FHeader^.maxTableSize * $0400);
FBlockReaded := True;
end;
procedure TParadoxDataset.ReadNextBlockHeader;
var
L : longint;
begin
if FaBlock^.nextBlock = 0 then exit; //last block
//Increment Blockstart
FaBlockStart := FaBlockStart+(FaBlock^.addDataSize div FHeader^.recordSize)+1;
FaRecord := FaBlockStart+1;
L := FaBlock^.nextBlock-1;
L := (L * FHeader^.maxTableSize * $0400) + FHeader^.headerSize;
FaBlockIdx := FaBlock^.nextBlock;
FBlockReaded := False;
FStream.Position := L;
FStream.Read(FaBlock^,6); //read only Block header
end;
procedure TParadoxDataset.ReadPrevBlockHeader;
var
L: LongWord;
begin
L := FaBlock^.prevBlock-1;
L := (L * FHeader^.maxTableSize * $0400) + FHeader^.headerSize;
FaBlockIdx := FaBlock^.prevBlock;
FBlockReaded := False;
FStream.Position := L;
FStream.Read(FaBlock^,6); //read only Block header
//decrement Blockstart
L := ((FaBlock^.addDataSize div FHeader^.recordSize)+1);
FaBlockStart := FaBlockStart-L;
FaRecord := FaBlockStart+1;
end;
procedure TParadoxDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
begin
//TODO
end;
function TParadoxDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
begin
Result := PRecInfo(Buffer + FHeader^.RecordSize)^.BookmarkFlag;
end;
procedure TParadoxDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
begin
PRecInfo(Buffer + FHeader^.RecordSize)^.BookmarkFlag := Value;
end;
procedure TParadoxDataset.SetFieldData(Field: TField; Buffer: Pointer);
begin
end;
procedure TParadoxDataset.SetFileName(const AValue: TFileName);
begin
if Active then
Close;
FFilename := AValue;
end;
procedure TParadoxDataset.SetRecNo(Value: Integer);
begin
if Value < FaRecord then
begin
while (Value <= FaBlockstart) do
ReadPrevBlockHeader;
FaRecord := Value;
end else
begin
while (Value > FaBlockstart+((FaBlock^.addDataSize div FHeader^.recordSize)+1)) do
ReadNextBlockHeader;
FaRecord := Value;
end;
end;
procedure TParadoxDataset.SetTargetEncoding(AValue: String);
begin
if AValue = FTargetEncoding then exit;
FTargetEncoding := Uppercase(AValue);
end;
end.