lazarus-ccr/components/tparadoxdataset/paradoxds.pas
2019-08-08 12:22:07 +00:00

957 lines
27 KiB
ObjectPascal

unit paradoxds;
{ TParadoxdataSet
Christian Ulrich christian@ullihome.de
License: LGPL
}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, db, lconvencoding, bufdataset_parser;
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;
FFilterBuffer : TRecordBuffer;
FParser: TBufDatasetParser;
function GetEncrypted: Boolean;
function GetInputEncoding: String; inline;
function GetPrimaryKeyFieldCount: Integer;
function GetTargetEncoding: String; inline;
function GetVersion: String;
function IsStoredTargetEncoding: Boolean;
function PxFilterRecord(Buffer: TRecordBuffer): Boolean;
function PxGetActiveBuffer(var Buffer: TRecordBuffer): 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 ParseFilter(const AFilter: string);
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
procedure SetFiltered(Value: Boolean); override;
procedure SetFilterText(const Value: String); override;
procedure SetRecNo(Value: Integer); override;
public
constructor Create(AOwner: TComponent); override;
function BookmarkValid(ABookmark: TBookmark): Boolean; override;
function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; 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;
property PrimaryKeyFieldCount: Integer read GetPrimaryKeyFieldCount;
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 Filter;
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
{ TParadoxDataset }
constructor TParadoxDataset.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHeader := nil;
FTargetEncoding := Uppercase(EncodingUTF8);
FInputEncoding := '';
BookmarkSize := SizeOf(LongInt);
end;
function TParadoxDataset.AllocRecordBuffer: PChar;
begin
if Assigned(Fheader) then
Result := AllocMem(GetRecordSize)
else
Result := nil;
end;
function TParadoxDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
begin
Result := Assigned(ABookmark) and (Length(ABookmark) <> 0);
end;
function TParadoxDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
var
idx1, idx2: LongWord;
begin
idx1 := PLongWord(Bookmark1)^;
idx2 := PLongWord(Bookmark2)^;
if idx1 > idx2 then
Result := +1
else
if idx1 = idx2 then
Result := 0
else
Result := -1
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
PLongWord(Data)^ := PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber;
end;
function TParadoxDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
begin
Result := PRecInfo(Buffer + FHeader^.RecordSize)^.BookmarkFlag;
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;
buf: TRecordBuffer = nil;
begin
Result := False;
if (RecordCount = 0) then
exit;
PXGetActiveBuffer(Buf);
p := buf + FPxFields[Field.FieldNo - 1].Offset;
F := FPxFields[Field.FieldNo - 1].Info;
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.GetPrimaryKeyFieldCount: Integer;
begin
if FHeader <> nil then
Result := FHeader^.primaryKeyFields
else
Result := 0;
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;
accepted: Boolean;
begin
Result := grOK;
accepted := false;
repeat
case GetMode of
gmNext:
begin
inc(FaRecord);
if (FaBlockIdx = FHeader^.lastBlock) and
(FaRecord > FaBlockStart + FaBlock^.addDataSize div FHeader^.recordSize + 1)
then
Result := grEOF
else
if FaRecord > FaBlockStart+1+(FaBlock^.addDataSize div FHeader^.recordSize) then
ReadNextBlockHeader;
end;
gmPrior:
begin
dec(FaRecord);
if (FaBlockIdx = FHeader^.firstBlock) and (FaRecord < 1) then
Result := grBOF
else
if FaRecord <= FaBlockStart then
begin
ReadPrevBlockHeader;
FaRecord := FaBlockStart+(FaBlock^.addDataSize div FHeader^.recordSize)+1;
end;
end;
gmCurrent:
if (FaRecord > RecordCount) or (FaRecord < 1) then
result := grError;
end;
if Result = grOK then begin
if not FBlockReaded then
ReadBlock;
L := ((faRecord - (FaBlockstart + 1))*FHeader^.recordSize) + 6;
if (faRecord - (FaBlockstart + 1)) >= 0 then
Move(PChar(FaBlock)[L],Buffer[0],FHeader^.recordSize)
else
Result := grError;
with PRecInfo(Buffer + FHeader^.recordSize)^ do begin
BookmarkFlag := bfCurrent;
RecordNumber := FaRecord;
end;
// Filtering
if Filtered then
accepted := PXFilterRecord(Buffer)
else
accepted := True;
if (GetMode = gmCurrent) and not accepted then
Result := grError;
end;
until (Result <> grOK) or Accepted;
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
FInputEncoding := '';
BindFields(FALSE);
if DefaultFields then // Destroy the TField
DestroyFields;
FreeMem(FHeader);
FreeMem(FaBlock);
FreeAndNil(FParser);
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
if BookmarkValid(ABookmark) then
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;
try
ParseFilter(Filter);
except
on E: Exception do
Filter := '';
end;
end;
procedure TParadoxDataset.InternalPost;
begin
end;
procedure TParadoxDataset.InternalSetToRecord(Buffer: PChar);
var
bm: LongWord;
begin
if (State <> dsInsert) then begin
bm := PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber;
InternalGotoBookmark(@bm);
end;
end;
function TParadoxDataset.IsCursorOpen: Boolean;
begin
Result := FActive;
end;
function TParadoxDataset.IsStoredTargetEncoding: Boolean;
begin
Result := not SameText(FTargetEncoding, EncodingUTF8);
end;
procedure TParadoxDataset.ParseFilter(const AFilter: string);
begin
if Length(AFilter) > 0 then
begin
if (FParser = nil) and IsCursorOpen then
FParser := TBufDatasetParser.Create(Self);
if FParser <> nil then
begin
FParser.PartialMatch := not (foNoPartialCompare in FilterOptions);
FParser.CaseInsensitive := foCaseInsensitive in FilterOptions;
FParser.ParseExpression(AFilter);
end;
end;
end;
function TParadoxDataset.PxFilterRecord(Buffer: TRecordBuffer): Boolean;
var
SaveState: TDatasetState;
begin
Result := True;
if not Assigned(OnFilterRecord) and not Filtered then
Exit;
SaveState := SetTempState(dsFilter);
Try
FFilterBuffer := Buffer;
If Assigned(OnFilterRecord) then
OnFilterRecord(Self, Result);
If Result and Filtered and (Filter <> '') then
Result := Boolean((FParser.ExtractFromBuffer(FFilterBuffer))^);
Finally
RestoreState(SaveState);
end;
end;
function TParadoxDataset.PxGetActiveBuffer(var Buffer: TRecordBuffer): Boolean;
begin
case State of
dsBrowse:
if IsEmpty then
Buffer := nil
else
Buffer := ActiveBuffer;
dsEdit,
dsInsert:
Buffer := ActiveBuffer;
dsFilter:
Buffer := FFilterBuffer;
else
Buffer := nil;
end;
Result := (Buffer <> nil);
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
// The BookMarkData is the RecNo: no need to set nothing;
{
if Data <> nil then
PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber := PLongWord(Data)^
else
PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber := 0;
}
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.SetFiltered(Value: Boolean);
begin
if (Value <> Filtered) then begin
inherited;
if IsCursorOpen then
Refresh;
end;
end;
procedure TParadoxDataset.SetFilterText(const Value: String);
begin
if (Value <> Filter) then begin
ParseFilter(Value);
inherited;
if IsCursorOpen and Filtered then
Refresh;
end;
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.