lazarus/lcl/lresources.pp
2024-11-23 08:10:58 +02:00

5859 lines
148 KiB
ObjectPascal

{
Author: Mattias Gaertner
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Abstract:
This unit maintains and stores all lazarus resources in the global list
named LazarusResources and provides methods and types to stream components.
A lazarus resource is an ansistring, with a name and a valuetype. Both, name
and valuetype, are ansistrings as well.
Lazarus resources are normally included via an include directive in the
initialization part of a unit. To create such include files use the
BinaryToLazarusResourceCode procedure.
To create a LRS file from an LFM file use the LFMtoLRSfile function which
transforms the LFM text to binary format and stores it as Lazarus resource
include file.
}
unit LResources;
{$mode objfpc}{$H+}
{ $DEFINE WideStringLenDoubled}
interface
uses
{$IFDEF Windows}
Windows,
{$ENDIF}
Classes, SysUtils, Types, RtlConsts, TypInfo, Variants,
// LCL
LCLStrConsts,
// LazUtils
LazConfigStorage, FPCAdds, DynQueue, LazUTF8, LazLoggerBase, LazTracer, LazUtilities;
{$DEFINE UseLRS}
{$DEFINE UseRES}
const
LRSComment = // do not translate this!
'This is an automatically generated lazarus resource file';
type
TFilerSignature = array[1..4] of Char;
{ TLResourceList }
TLResource = class
public
Name: AnsiString;
ValueType: AnsiString;
Value: AnsiString;
end;
TLResourceList = class(TObject)
private
FList: TList; // main list with all resource pointers
FMergeList: TList; // list needed for mergesort
FSortedCount: integer; // 0 .. FSortedCount-1 resources are sorted
function FindPosition(const Name: AnsiString):integer;
function GetItems(Index: integer): TLResource;
procedure Sort;
procedure MergeSort(List, MergeList: TList; Pos1, Pos2: integer);
procedure Merge(List, MergeList: TList; Pos1, Pos2, Pos3: integer);
public
constructor Create;
destructor Destroy; override;
procedure Add(const Name, ValueType, Value: AnsiString);
procedure Add(const Name, ValueType: AnsiString; const Values: array of string);
function Find(const Name: AnsiString): TLResource; overload;
function Find(const Name, ValueType: AnsiString): TLResource; overload;
function Count: integer;
property Items[Index: integer]: TLResource read GetItems;
end;
{ TLazarusResourceStream }
TLazarusResourceStream = class(TCustomMemoryStream)
private
FLRes: TLResource;
{$ifdef UseRES}
FPRes: TFPResourceHGLOBAL;
{$endif}
procedure Initialize(Name, ResType: PChar);
public
constructor Create(const ResName: string; ResType: PChar);
constructor CreateFromID(ResID: Integer; ResType: PChar);
constructor CreateFromHandle(AHandle: TLResource); overload;
{$ifdef UseRES}
// here from FP resource handle
constructor CreateFromHandle(Instance: TFPResourceHMODULE; AHandle: TFPResourceHandle); overload;
{$endif}
destructor Destroy; override;
function Write(const Buffer; Count: Longint): Longint; override;
property Res: TLResource read FLRes;
end;
{ TAbstractTranslator}
TAbstractTranslator = class(TObject)//Should it be somewhat more than TObject?
public
procedure TranslateStringProperty(Sender:TObject; const Instance: TPersistent; PropInfo: PPropInfo; var Content:string); virtual;abstract;
//seems like we need nothing more here
end;
var LRSTranslator: TAbstractTranslator;
type
TLRSItemType = (
lrsitCollection,
lrsitComponent,
lrsitList,
lrsitProperty
);
TLRSORStackItem = record
Name: string;
ItemType: TLRSItemType;
Root: TComponent;
PushCount: integer; // waiting for this number of Pop
ItemNr: integer; // nr in a collection or list
end;
PLRSORStackItem = ^TLRSORStackItem;
{ TLRSObjectReader }
TLRSObjectReader = class(TAbstractObjectReader)
private
FStream: TStream;
FBuffer: Pointer;
FBufSize: Integer;
FBufPos: Integer;
FBufEnd: Integer;
FStack: PLRSORStackItem;
FStackPointer: integer;
FStackCapacity: integer;
FReader: TReader;
procedure SkipProperty;
procedure SkipSetBody;
procedure Push(ItemType: TLRSItemType; const AName: string = '';
Root: TComponent = nil; PushCount: integer = 1);
procedure Pop;
procedure ClearStack;
function InternalReadValue: TValueType;
procedure EndPropertyIfOpen;
protected
function ReadIntegerContent: integer;
public
constructor Create(AStream: TStream; BufSize: Integer); virtual;
destructor Destroy; override;
function NextValue: TValueType; override;
function ReadValue: TValueType; override;
procedure BeginRootComponent; override;
procedure BeginComponent(var Flags: TFilerFlags; var AChildPos: Integer;
var CompClassName, CompName: String); override;
function BeginProperty: String; override;
function GetStackPath: string;
procedure Read(var Buf; Count: LongInt); override;
procedure ReadBinary(const DestData: TMemoryStream); override;
function ReadFloat: Extended; override;
function ReadSingle: Single; override;
function ReadCurrency: Currency; override;
function ReadDate: TDateTime; override;
function ReadIdent(ValueType: TValueType): String; override;
function ReadInt8: ShortInt; override;
function ReadInt16: SmallInt; override;
function ReadInt32: LongInt; override;
function ReadInt64: Int64; override;
function ReadSet(EnumType: Pointer): Integer; override;
procedure ReadSignature; override;
function ReadStr: String; override;
function ReadString(StringType: TValueType): String; override;
function ReadWideString: WideString; override;
function ReadUnicodeString: UnicodeString; override;
procedure SkipComponent(SkipComponentInfos: Boolean); override;
procedure SkipValue; override;
public
property Stream: TStream read FStream;
property Reader: TReader read FReader write FReader;
end;
TLRSObjectReaderClass = class of TLRSObjectReader;
{ TLRSOWStackItem
The TLRSObjectWriter can find empty entries and omit writing them to stream.
For example:
inline ConditionalOptionsFrame: TCompOptsConditionalsFrame
inherited COCTreeView: TTreeView
end
inherited COCPopupMenu: TPopupMenu
end
end
The empty inherited child components will not be written if
WriteEmptyInheritedChilds = false (default).
Reason:
This allows one to delete/rename controls in ancestors without the need
to update all descendants.
}
TLRSOWStackItemState = (
lrsowsisStarted, // now writing header
lrsowsisHeaderWritten, // header saved on stack, not yet written to stream, waiting for data
lrsowsisDataWritten // header written to stream, data written
);
TLRSOWStackItem = record
Name: string;
ItemType: TLRSItemType;
Root: TComponent;
PushCount: integer; // waiting for this number of Pop
ItemNr: integer; // nr in a collection or list
SkipIfEmpty: boolean;
State: TLRSOWStackItemState;
Buffer: Pointer;
BufCount: PtrInt;
BufCapacity: PtrInt;
end;
PLRSOWStackItem = ^TLRSOWStackItem;
{$IF FPC_FULLVERSION>30300}
TLazObjectWriterString = RawByteString;
{$ELSE}
TLazObjectWriterString = String;
{$ENDIF}
{ TLRSObjectWriter }
TLRSObjectWriter = class(TAbstractObjectWriter)
private
FStream: TStream;
FBuffer: Pointer;
FBufSize: Integer;
FBufPos: Integer;
FSignatureWritten: Boolean;
FStack: PLRSOWStackItem;
FStackPointer: integer;
FStackCapacity: integer;
FWriteEmptyInheritedChilds: boolean;
FWriter: TWriter;
procedure Push(ItemType: TLRSItemType; const AName: string = '';
Root: TComponent = nil; PushCount: integer = 1;
SkipIfEmpty: boolean = false);
procedure EndHeader;
procedure Pop(WriteNull: boolean);
procedure ClearStack;
procedure FlushStackToStream;
procedure WriteToStream(const Buffer; Count: Longint);
protected
procedure FlushBuffer; override;
procedure WriteValue(Value: TValueType);
procedure WriteStr(const Value: String);
procedure WriteIntegerContent(i: integer);
procedure WriteWordContent(w: word);
procedure WriteInt64Content(i: int64);
procedure WriteSingleContent(s: single);
procedure WriteDoubleContent(d: Double);
procedure WriteExtendedContent(e: Extended);
procedure WriteCurrencyContent(c: Currency);
procedure WriteWideStringContent(const ws: WideString);
procedure WriteWordsReversed(p: PWord; Count: integer);
procedure WriteNulls(Count: integer);
public
constructor Create(Stream: TStream; BufSize: Integer); virtual;
destructor Destroy; override;
{ Begin/End markers. Those ones who don't have an end indicator, use
"EndList", after the occurrence named in the comment. Note that this
only counts for "EndList" calls on the same level; each BeginXXX call
increases the current level. }
procedure BeginCollection; override;{ Ends with the next "EndList" }
procedure BeginComponent(Component: TComponent; Flags: TFilerFlags;
ChildPos: Integer); override; { Ends after the second "EndList" }
procedure WriteSignature; override;
procedure BeginList; override;
procedure EndList; override;
procedure BeginProperty(const PropName: String); override;
procedure EndProperty; override;
function GetStackPath: string;
procedure Write(const Buffer; Count: Longint); override;
procedure WriteBinary(const Buffer; Count: LongInt); override;
procedure WriteBoolean(Value: Boolean); override;
procedure WriteFloat(const Value: Extended); override;
procedure WriteSingle(const Value: Single); override;
procedure WriteCurrency(const Value: Currency); override;
procedure WriteDate(const Value: TDateTime); override;
procedure WriteIdent(const Ident: string); override;
procedure WriteInteger(Value: Int64); override;
procedure WriteMethodName(const Name: String); override;
procedure WriteSet(Value: LongInt; SetType: Pointer); override;
procedure WriteString(const Value: TLazObjectWriterString); override;
procedure WriteWideString(const Value: WideString); override;
procedure WriteUnicodeString(const Value: UnicodeString); override;
procedure WriteVariant(const Value: Variant); override;
property WriteEmptyInheritedChilds: boolean read FWriteEmptyInheritedChilds write FWriteEmptyInheritedChilds;
property Writer: TWriter read FWriter write FWriter;
end;
TLRSObjectWriterClass = class of TLRSObjectWriter;
TLRPositionLink = record
LFMPosition: int64;
LRSPosition: int64;
Data: Pointer;
end;
PLRPositionLink = ^TLRPositionLink;
{ TLRPositionLinks }
TLRPositionLinks = class
private
FItems: TFPList;
FCount: integer;
function GetData(Index: integer): Pointer;
function GetLFM(Index: integer): Int64;
function GetLRS(Index: integer): Int64;
procedure SetCount(const AValue: integer);
procedure SetData(Index: integer; const AValue: Pointer);
procedure SetLFM(Index: integer; const AValue: Int64);
procedure SetLRS(Index: integer; const AValue: Int64);
public
constructor Create;
destructor Destroy; override;
procedure Clear;
procedure Sort(LFMPositions: Boolean);
function IndexOf(const Position: int64; LFMPositions: Boolean): integer;
function IndexOfRange(const FromPos, ToPos: int64;
LFMPositions: Boolean): integer;
procedure SetPosition(const FromPos, ToPos, MappedPos: int64;
LFMtoLRSPositions: Boolean);
procedure Add(const LFMPos, LRSPos: Int64; AData: Pointer);
public
property LFM[Index: integer]: int64 read GetLFM write SetLFM;
property LRS[Index: integer]: int64 read GetLRS write SetLRS;
property Data[Index: integer]: Pointer read GetData write SetData;
property Count: integer read FCount write SetCount;
end;
{ TUTF8Parser }
TUTF8Parser = class(TObject)
private
fStream : TStream;
fBuf : pchar;
fBufLen : integer; // read
fPos : integer;
fLineStart : integer; // column = fPos - fLineStart + 1
fFloatType : char;
fSourceLine : integer;
fToken : char;
fEofReached : boolean;
fLastTokenStr : string;
function GetTokenName(aTok : char) : string;
procedure LoadBuffer;
procedure CheckLoadBuffer; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
procedure ProcessChar; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
function IsNumber : boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
function IsHexNum : boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
function IsAlpha : boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
function IsAlphaNum : boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
function GetHexValue(c : char) : byte; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
function GetAlphaNum : string;
procedure HandleNewLine;
procedure SkipSpaces;
procedure SkipWhitespace;
procedure HandleEof;
procedure HandleAlphaNum;
procedure HandleNumber;
procedure HandleHexNumber;
function HandleQuotedString: string;
function HandleDecimalString: string;
procedure HandleString;
procedure HandleMinus;
procedure HandleUnknown;
public
constructor Create(Stream: TStream);
destructor Destroy; override;
procedure CheckToken(T: Char);
procedure CheckTokenSymbol(const S: string);
procedure Error(const Ident: string);
procedure ErrorFmt(const Ident: string; const Args: array of const);
procedure ErrorStr(const Message: string);
procedure HexToBinary(Stream: TStream);
function NextToken: Char;
function SourcePos: Longint;
function TokenComponentIdent: string;
function TokenFloat: Extended;
function TokenInt: Int64;
function TokenString: string;
function TokenSymbolIs(const S: string): Boolean;
property FloatType: Char read fFloatType;
property SourceLine: Integer read fSourceLine;
function SourceColumn: integer;
property Token: Char read fToken;
end deprecated 'use Classes.TParser instead';
{ TCustomLazComponentQueue
A queue to stream components, used for multithreading or network.
The function ConvertComponentAsString converts a component to binary format
with a leading size information (using WriteLRSInt64MB).
When streaming components over network, they will arrive in chunks.
TCustomLazComponentQueue tells you, if a whole component has arrived and if
it has completely arrived. }
TCustomLazComponentQueue = class(TComponent)
private
FOnFindComponentClass: TFindComponentClassEvent;
protected
FQueue: TDynamicDataQueue;
function ReadComponentSize(out ComponentSize, SizeLength: int64): Boolean; virtual;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
function Write(const Buffer; Count: Longint): Longint;
function CopyFrom(AStream: TStream; Count: Longint): Longint;
function HasComponent: Boolean; virtual;
function ReadComponent(var AComponent: TComponent;
NewOwner: TComponent = nil): Boolean; virtual;
function ConvertComponentAsString(AComponent: TComponent): string;
property OnFindComponentClass: TFindComponentClassEvent
read FOnFindComponentClass write FOnFindComponentClass;
end;
{ TLazComponentQueue }
TLazComponentQueue = class(TCustomLazComponentQueue)
published
property Name;
property OnFindComponentClass;
end;
TPropertyToSkip = record
PersistentClass: TPersistentClass;
PropertyName: String;
Note: String;
HelpKeyword: String;
end;
PRemovedProperty = ^TPropertyToSkip;
{ TPropertyToSkipList }
TPropertiesToSkip = class(TList)
private
function GetItem(AIndex: Integer): PRemovedProperty;
procedure SetItem(AIndex: Integer; const AValue: PRemovedProperty);
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
procedure DoPropertyNotFound(Reader: TReader; Instance: TPersistent;
var PropName: string; IsPath: boolean; var Handled, Skip: Boolean);
public
function IndexOf(AInstance: TPersistent; const APropertyName: String): Integer; overload;
function IndexOf(AClass: TPersistentClass; APropertyName: String): Integer; overload;
function Add(APersistentClass: TPersistentClass; const APropertyName, ANote,
AHelpKeyWord: string): Integer; reintroduce;
property Items[AIndex: Integer]: PRemovedProperty read GetItem write SetItem;
end;
const
ObjStreamMaskInherited = 1;
ObjStreamMaskChildPos = 2;
ObjStreamMaskInline = 4;
var
LazarusResources: TLResourceList;
PropertiesToSkip: TPropertiesToSkip = nil;
LRSObjectReaderClass: TLRSObjectReaderClass=TLRSObjectReader;
LRSObjectWriterClass: TLRSObjectWriterClass=TLRSObjectWriter;
function InitResourceComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
function InitLazResourceComponent(Instance: TComponent;
RootAncestor: TClass): Boolean;
function CreateLRSReader(s: TStream; var DestroyDriver: boolean): TReader;
function CreateLRSWriter(s: TStream; var DestroyDriver: boolean): TWriter;
function GetClassNameFromLRSStream(s: TStream; out IsInherited: Boolean): shortstring;
procedure GetComponentInfoFromLRSStream(s: TStream;
out ComponentName, ComponentClassName: string;
out IsInherited: Boolean);
procedure WriteComponentAsBinaryToStream(AStream: TStream;
AComponent: TComponent);
procedure ReadComponentFromBinaryStream(AStream: TStream;
var RootComponent: TComponent;
OnFindComponentClass: TFindComponentClassEvent;
TheOwner: TComponent = nil;
Parent: TComponent = nil;
ReaderRoot: TComponent = nil);
procedure WriteComponentAsTextToStream(AStream: TStream;
AComponent: TComponent);
procedure ReadComponentFromTextStream(AStream: TStream;
var RootComponent: TComponent;
OnFindComponentClass: TFindComponentClassEvent;
TheOwner: TComponent = nil;
Parent: TComponent = nil);
procedure SaveComponentToConfig(Config: TConfigStorage; const Path: string;
AComponent: TComponent);
procedure LoadComponentFromConfig(Config: TConfigStorage; const Path: string;
var RootComponent: TComponent;
OnFindComponentClass: TFindComponentClassEvent;
TheOwner: TComponent = nil;
Parent: TComponent = nil);
function CompareComponents(Component1, Component2: TComponent): boolean;
function CompareMemStreams(Stream1, Stream2: TCustomMemoryStream): boolean;
procedure BinaryToLazarusResourceCode(BinStream, ResStream: TStream;
const ResourceName, ResourceType: String);
function LFMtoLRSfile(const LFMfilename: string): boolean;// true on success
function LFMtoLRSstream(LFMStream, LRSStream: TStream): boolean;// true on success
function FindLFMClassName(LFMStream: TStream):AnsiString;
procedure ReadLFMHeader(LFMStream: TStream;
out LFMType, LFMComponentName, LFMClassName: String);
procedure ReadLFMHeader(const LFMSource: string;
out LFMClassName: String; out LFMType: String);
procedure ReadLFMHeader(const LFMSource: string;
out LFMType, LFMComponentName, LFMClassName: String);
function ReadLFMHeaderFromFile(const Filename: string;
out LFMType, LFMComponentName, LFMClassName: String): boolean;
function CreateLFMFile(AComponent: TComponent; LFMStream: TStream): integer;
function SameLFMTypeName(aUnitname, aTypename, LFMTypename: string): boolean;
type
TLRSStreamOriginalFormat = (sofUnknown, sofBinary, sofText);
procedure LRSObjectBinaryToText(Input, Output: TStream); // binary to lfm
procedure LRSObjectTextToBinary(Input, Output: TStream; // lfm to binary
Links: TLRPositionLinks = nil);
procedure LRSObjectToText(Input, Output: TStream;
var OriginalFormat: TLRSStreamOriginalFormat);
procedure LRSObjectResourceToText(Input, Output: TStream); // lrs to lfm
procedure LRSObjectResToText(Input, Output: TStream;
var OriginalFormat: TLRSStreamOriginalFormat);
function TestFormStreamFormat(Stream: TStream): TLRSStreamOriginalFormat;
procedure FormDataToText(FormStream, TextStream: TStream;
aFormat: TLRSStreamOriginalFormat = sofUnknown);
function FindResourceLFM(ResName: string): HRSRC;
procedure DefineRectProperty(Filer: TFiler; const Name: string;
ARect, DefaultRect: PRect);
procedure ReverseBytes(p: Pointer; Count: integer);
procedure ReverseByteOrderInWords(p: PWord; Count: integer);
function ConvertLRSExtendedToDouble(p: Pointer): Double;
procedure ConvertEndianBigDoubleToLRSExtended(BigEndianDouble,
LRSExtended: Pointer);
procedure ConvertLEDoubleToLRSExtended(LEDouble, LRSExtended: Pointer);
function ReadLRSShortInt(s: TStream): shortint;
function ReadLRSByte(s: TStream): byte;
function ReadLRSSmallInt(s: TStream): smallint;
function ReadLRSWord(s: TStream): word;
function ReadLRSInteger(s: TStream): integer;
function ReadLRSCardinal(s: TStream): cardinal;
function ReadLRSInt64(s: TStream): int64;
function ReadLRSSingle(s: TStream): Single;
function ReadLRSDouble(s: TStream): Double;
function ReadLRSExtended(s: TStream): Extended;
function ReadLRSCurrency(s: TStream): Currency;
function ReadLRSWideString(s: TStream): WideString;
function ReadLRSEndianLittleExtendedAsDouble(s: TStream): Double;
function ReadLRSValueType(s: TStream): TValueType;
function ReadLRSInt64MB(s: TStream): int64;// multibyte
procedure WriteLRSSmallInt(s: TStream; const i: smallint);
procedure WriteLRSWord(s: TStream; const w: word);
procedure WriteLRSInteger(s: TStream; const i: integer);
procedure WriteLRSCardinal(s: TStream; const c: cardinal);
procedure WriteLRSSingle(s: TStream; const si: Single);
procedure WriteLRSDouble(s: TStream; const d: Double);
procedure WriteLRSExtended(s: TStream; const e: extended);
procedure WriteLRSInt64(s: TStream; const i: int64);
procedure WriteLRSCurrency(s: TStream; const c: Currency);
procedure WriteLRSWideStringContent(s: TStream; const w: WideString);
procedure WriteLRSInt64MB(s: TStream; const Value: int64);// multibyte
procedure WriteLRSReversedWord(s: TStream; w: word);
procedure WriteLRS4BytesReversed(s: TStream; p: Pointer);
procedure WriteLRS8BytesReversed(s: TStream; p: Pointer);
procedure WriteLRS10BytesReversed(s: TStream; p: Pointer);
procedure WriteLRSNull(s: TStream; Count: integer);
procedure WriteLRSEndianBigDoubleAsEndianLittleExtended(s: TStream;
EndBigDouble: PByte);
procedure WriteLRSDoubleAsExtended(s: TStream; ADouble: PByte);
procedure WriteLRSReversedWords(s: TStream; p: Pointer; Count: integer);
function FloatToLFMStr(const Value: extended; Precision, Digits: Integer
): string;
function CompareLRPositionLinkWithLFMPosition(Item1, Item2: Pointer): integer;
function CompareLRPositionLinkWithLRSPosition(Item1, Item2: Pointer): integer;
procedure RegisterPropertyToSkip(PersistentClass: TPersistentClass;
const PropertyName, Note, HelpKeyWord: string);
procedure Register;
implementation
const
LineEnd: ShortString = LineEnding;
var
ByteToStr: array[char] of shortstring;
ByteToStrValid: boolean=false;
type
{ TDefineRectPropertyClass }
TDefineRectPropertyClass = class
public
Value: PRect;
DefaultValue: PRect;
constructor Create(AValue, ADefaultRect: PRect);
procedure ReadData(Reader: TReader);
procedure WriteData(Writer: TWriter);
function HasData: Boolean;
end;
{ TReaderUniqueNamer - dummy class, used by the reader functions to rename
components, that are read from a stream, on the fly. }
TReaderUniqueNamer = class
procedure OnSetName(Reader: TReader; Component: TComponent;
var Name: string);
end;
{ TPropertiesToSkip }
function TPropertiesToSkip.GetItem(AIndex: Integer): PRemovedProperty;
begin
Result := inherited Get(AIndex);
end;
procedure TPropertiesToSkip.SetItem(AIndex: Integer;
const AValue: PRemovedProperty);
begin
inherited Put(AIndex, AValue);
end;
procedure TPropertiesToSkip.Notify(Ptr: Pointer; Action: TListNotification);
begin
if Action = lnDeleted then
Dispose(PRemovedProperty(Ptr))
else
inherited Notify(Ptr, Action);
end;
procedure TPropertiesToSkip.DoPropertyNotFound(Reader: TReader; Instance: TPersistent;
var PropName: string; IsPath: boolean; var Handled, Skip: Boolean);
begin
Skip := IndexOf(Instance, PropName) >= 0;
Handled := Skip;
end;
function TPropertiesToSkip.IndexOf(AInstance: TPersistent;
const APropertyName: String): Integer;
begin
if AInstance <> nil then
Result := IndexOf(TPersistentClass(AInstance.ClassType), APropertyName)
else
Result := -1;
end;
function TPropertiesToSkip.IndexOf(AClass: TPersistentClass;
APropertyName: String): Integer;
var
PropertyInfo: PRemovedProperty;
begin
APropertyName := LowerCase(APropertyName);
Result := Count - 1;
while Result >= 0 do
begin
PropertyInfo := Items[Result];
if AClass.InheritsFrom(PropertyInfo^.PersistentClass) and
(APropertyName = PropertyInfo^.PropertyName) then
begin
Exit;
end;
Dec(Result);
end;
Result := -1;
end;
function TPropertiesToSkip.Add(APersistentClass: TPersistentClass;
const APropertyName, ANote, AHelpKeyWord: string): Integer;
var
Item: PRemovedProperty;
begin
Result := IndexOf(APersistentClass, APropertyName);
if Result = -1 then
begin
New(Item);
Item^.PersistentClass := APersistentClass;
Item^.PropertyName := LowerCase(APropertyName);
Item^.Note := ANote;
Item^.HelpKeyword := AHelpKeyWord;
Result := inherited Add(Item);
end;
end;
{ TReaderUniqueNamer }
procedure TReaderUniqueNamer.OnSetName(Reader: TReader; Component: TComponent;
var Name: string);
procedure MakeValidIdentifier;
var
i: Integer;
begin
for i:=length(Name) downto 1 do
if not (Name[i] in ['0'..'9','_','a'..'z','A'..'Z']) then
System.Delete(Name,i,1);
if (Name<>'') and (Name[1] in ['0'..'9']) then
Name:='_'+Name;
end;
function NameIsUnique: Boolean;
var
Owner: TComponent;
i: Integer;
CurComponent: TComponent;
begin
Result:=true;
if Name='' then exit;
Owner:=Component.Owner;
if Owner=nil then exit;
for i:=0 to Owner.ComponentCount-1 do begin
CurComponent:=Owner.Components[i];
if CurComponent=Component then continue;
if CompareText(CurComponent.Name,Name)=0 then exit(false);
end;
end;
begin
MakeValidIdentifier;
while not NameIsUnique do
Name:=CreateNextIdentifier(Name);
end;
{ TDefineRectPropertyClass }
constructor TDefineRectPropertyClass.Create(AValue, ADefaultRect: PRect);
begin
Value:=AValue;
DefaultValue:=ADefaultRect;
end;
procedure TDefineRectPropertyClass.ReadData(Reader: TReader);
begin
with Reader do begin
ReadListBegin;
Value^.Left:=ReadInteger;
Value^.Top:=ReadInteger;
Value^.Right:=ReadInteger;
Value^.Bottom:=ReadInteger;
ReadListEnd;
end;
end;
procedure TDefineRectPropertyClass.WriteData(Writer: TWriter);
begin
with Writer do begin
WriteListBegin;
WriteInteger(Value^.Left);
WriteInteger(Value^.Top);
WriteInteger(Value^.Right);
WriteInteger(Value^.Bottom);
WriteListEnd;
end;
end;
function TDefineRectPropertyClass.HasData: Boolean;
begin
if DefaultValue<>nil then begin
Result:=(DefaultValue^.Left<>Value^.Left)
or (DefaultValue^.Top<>Value^.Top)
or (DefaultValue^.Right<>Value^.Right)
or (DefaultValue^.Bottom<>Value^.Bottom);
end else begin
Result:=(Value^.Left<>0)
or (Value^.Top<>0)
or (Value^.Right<>0)
or (Value^.Bottom<>0);
end;
end;
function InitResourceComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
begin
Result := InitLazResourceComponent(Instance, RootAncestor);
end;
function FindResourceLFM(ResName: string): HRSRC;
{$if defined(WinCE)}
var
u: UnicodeString;
begin
u:=ResName;
Result := FindResource(HInstance,PWideChar(u),Windows.RT_RCDATA);
end;
{$else}
begin
Result := FindResource(HInstance,PChar(ResName),
{$ifdef Windows}Windows.{$endif}RT_RCDATA);
end;
{$endif}
procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect,
DefaultRect: PRect);
var
PropDef: TDefineRectPropertyClass;
begin
PropDef := TDefineRectPropertyClass.Create(ARect, DefaultRect);
try
Filer.DefineProperty(Name,@PropDef.ReadData,@PropDef.WriteData,PropDef.HasData);
finally
PropDef.Free;
end;
end;
procedure InitByteToStr;
var
c: Char;
begin
if ByteToStrValid then exit;
for c:=Low(char) to High(char) do
ByteToStr[c]:=IntToStr(ord(c));
ByteToStrValid:=true;
end;
function GetClassNameFromLRSStream(s: TStream; out IsInherited: Boolean
): shortstring;
var
Signature: TFilerSignature;
NameLen: byte;
OldPosition: Int64;
begin
Result:='';
OldPosition:=s.Position;
// read signature
Signature:='1234';
s.Read(Signature[1],length(Signature));
if Signature<>FilerSignature then exit;
// read classname length
NameLen:=0;
s.Read(NameLen,1);
if (NameLen and $f0) = $f0 then begin
// this was the Flag Byte
IsInherited := (NameLen and ObjStreamMaskInherited) <> 0;
// read namelen
s.Read(NameLen,1);
end else
IsInherited := False;
// read classname
if NameLen>0 then begin
SetLength(Result,NameLen);
s.Read(Result[1],NameLen);
end;
s.Position:=OldPosition;
end;
procedure GetComponentInfoFromLRSStream(s: TStream; out ComponentName,
ComponentClassName: string; out IsInherited: Boolean);
var
Signature: TFilerSignature;
NameLen: byte;
OldPosition: Int64;
Flag: Byte;
begin
ComponentName:='';
ComponentClassName:='';
OldPosition:=s.Position;
// read signature
Signature:='1234';
s.Read(Signature[1],length(Signature));
if Signature<>FilerSignature then exit;
// read classname length
NameLen:=0;
s.Read(NameLen,1);
if (NameLen and $f0) = $f0 then begin
// Read Flag Byte
Flag:=NameLen;
IsInherited := (Flag and ObjStreamMaskInherited) <> 0;
s.Read(NameLen,1);
end else
IsInherited := False;
// read classname
if NameLen>0 then begin
SetLength(ComponentClassName,NameLen);
s.Read(ComponentClassName[1],NameLen);
end;
// read component name length
NameLen:=0;
s.Read(NameLen,1);
// read componentname
if NameLen>0 then begin
SetLength(ComponentName,NameLen);
s.Read(ComponentName[1],NameLen);
end;
s.Position:=OldPosition;
end;
procedure WriteComponentAsBinaryToStream(AStream: TStream;
AComponent: TComponent);
var
Writer: TWriter;
DestroyDriver: Boolean;
begin
DestroyDriver:=false;
Writer:=nil;
try
Writer:=CreateLRSWriter(AStream,DestroyDriver);
Writer.WriteDescendent(AComponent,nil);
finally
if DestroyDriver then
Writer.Driver.Free;
Writer.Free;
end;
end;
procedure ReadComponentFromBinaryStream(AStream: TStream;
var RootComponent: TComponent;
OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent;
Parent: TComponent; ReaderRoot: TComponent);
var
DestroyDriver: Boolean;
Reader: TReader;
IsInherited: Boolean;
AClassName: String;
AClass: TComponentClass;
UniqueNamer: TReaderUniqueNamer;
begin
// get root class
AClassName:=GetClassNameFromLRSStream(AStream,IsInherited);
if IsInherited then begin
// inherited is not supported by this simple function
{$IFNDEF DisableChecks}
DebugLn('ReadComponentFromBinaryStream WARNING: "inherited" is not supported by this simple function');
{$ENDIF}
end;
AClass:=nil;
OnFindComponentClass(nil,AClassName,AClass);
if AClass=nil then
raise EClassNotFound.CreateFmt('Class "%s" not found', [AClassName]);
if RootComponent=nil then begin
// create root component
// first create the new instance and set the variable ...
RootComponent:=AClass.NewInstance as TComponent;
// then call the constructor
RootComponent.Create(TheOwner);
end else begin
// there is a root component, check if class is compatible
if not RootComponent.InheritsFrom(AClass) then begin
raise EComponentError.CreateFmt('Cannot assign a %s to a %s.',
[AClassName,RootComponent.ClassName]);
end;
end;
// read the root component
DestroyDriver:=false;
Reader:=nil;
UniqueNamer:=nil;
try
UniqueNamer:=TReaderUniqueNamer.Create;
Reader:=CreateLRSReader(AStream,DestroyDriver);
if ReaderRoot = nil then
Reader.Root:=RootComponent
else
Reader.Root:=ReaderRoot;
Reader.Owner:=TheOwner;
Reader.Parent:=Parent;
Reader.OnFindComponentClass:=OnFindComponentClass;
Reader.OnSetName:=@UniqueNamer.OnSetName;
Reader.BeginReferences;
try
Reader.Driver.BeginRootComponent;
RootComponent:=Reader.ReadComponent(RootComponent);
Reader.FixupReferences;
finally
Reader.EndReferences;
end;
finally
if DestroyDriver then
Reader.Driver.Free;
UniqueNamer.Free;
Reader.Free;
end;
end;
procedure WriteComponentAsTextToStream(AStream: TStream; AComponent: TComponent);
var
BinStream: TMemoryStream;
begin
BinStream:=nil;
try
BinStream:=TMemoryStream.Create;
WriteComponentAsBinaryToStream(BinStream,AComponent);
BinStream.Position:=0;
LRSObjectBinaryToText(BinStream,AStream);
finally
BinStream.Free;
end;
end;
procedure ReadComponentFromTextStream(AStream: TStream;
var RootComponent: TComponent;
OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent;
Parent: TComponent);
var
BinStream: TMemoryStream;
begin
BinStream:=nil;
try
BinStream:=TMemoryStream.Create;
LRSObjectTextToBinary(AStream,BinStream);
BinStream.Position:=0;
ReadComponentFromBinaryStream(BinStream,RootComponent,OnFindComponentClass,
TheOwner,Parent);
finally
BinStream.Free;
end;
end;
procedure SaveComponentToConfig(Config: TConfigStorage; const Path: string;
AComponent: TComponent);
var
BinStream: TMemoryStream;
TxtStream: TMemoryStream;
s: string;
begin
BinStream:=nil;
TxtStream:=nil;
try
// write component to stream
BinStream:=TMemoryStream.Create;
WriteComponentAsBinaryToStream(BinStream,AComponent);
// convert it to human readable text format
BinStream.Position:=0;
TxtStream:=TMemoryStream.Create;
LRSObjectBinaryToText(BinStream,TxtStream);
// convert stream to string
SetLength(s,TxtStream.Size);
TxtStream.Position:=0;
if s<>'' then
TxtStream.Read(s[1],length(s));
// write to config
Config.SetDeleteValue(Path,s,'');
finally
BinStream.Free;
TxtStream.Free;
end;
end;
procedure LoadComponentFromConfig(Config: TConfigStorage; const Path: string;
var RootComponent: TComponent;
OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent;
Parent: TComponent);
var
s: String;
TxtStream: TMemoryStream;
begin
// read from config
s:=Config.GetValue(Path,'');
TxtStream:=nil;
try
TxtStream:=TMemoryStream.Create;
if s<>'' then
TxtStream.Write(s[1],length(s));
TxtStream.Position:=0;
// create component from stream
ReadComponentFromTextStream(TxtStream,RootComponent,OnFindComponentClass,
TheOwner,Parent);
finally
TxtStream.Free;
end;
end;
function CompareComponents(Component1, Component2: TComponent): boolean;
var
Stream1: TMemoryStream;
Stream2: TMemoryStream;
i: Integer;
begin
if Component1=Component2 then exit(true);
Result:=false;
// quick checks
if (Component1=nil) or (Component2=nil) then exit;
if (Component1.ClassType<>Component2.ClassType) then exit;
if Component1.ComponentCount<>Component2.ComponentCount then exit;
for i:=0 to Component1.ComponentCount-1 do begin
if Component1.Components[i].ClassType<>Component2.Components[i].ClassType
then exit;
end;
// expensive streaming test
try
Stream1:=nil;
Stream2:=nil;
try
Stream1:=TMemoryStream.Create;
WriteComponentAsBinaryToStream(Stream1,Component1);
Stream2:=TMemoryStream.Create;
WriteComponentAsBinaryToStream(Stream2,Component2);
Result:=CompareMemStreams(Stream1,Stream2);
finally
Stream1.Free;
Stream2.Free;
end;
except
end;
end;
function CompareMemStreams(Stream1, Stream2: TCustomMemoryStream
): boolean;
var
p1: Pointer;
p2: Pointer;
Cnt: Int64;
CurCnt: cardinal;
begin
if Stream1=Stream2 then exit(true);
Result:=false;
if (Stream1=nil) or (Stream2=nil) then exit;
if Stream1.Size<>Stream2.Size then exit;
Cnt:=Stream1.Size;
p1:=Stream1.Memory;
p2:=Stream2.Memory;
while Cnt>0 do begin
CurCnt:=Cnt;
if CurCnt>=High(Cardinal) then CurCnt:=High(Cardinal);
if not CompareMem(p1,p2,CurCnt) then exit;
inc(p1,CurCnt);
inc(p2,CurCnt);
dec(Cnt,CurCnt);
end;
Result:=true;
end;
procedure BinaryToLazarusResourceCode(BinStream,ResStream:TStream;
const ResourceName, ResourceType: String);
{ example ResStream:
LazarusResources.Add('ResourceName','ResourceType',
#123#45#34#78#18#72#45#34#78#18#72#72##45#34#78#45#34#78#184#34#78#145#34#78
+#83#187#6#78#83
);
}
const
ReadBufSize = 4096;
WriteBufSize = 4096;
var
s, Indent: string;
x: integer;
c: char;
RangeString, NewRangeString: boolean;
RightMargin, CurLine: integer;
WriteBufStart, Writebuf: PChar;
WriteBufPos: Integer;
ReadBufStart, ReadBuf: PChar;
ReadBufPos, ReadBufLen: integer;
MinCharCount: Integer;
procedure FillReadBuf;
begin
ReadBuf:=ReadBufStart;
ReadBufPos:=0;
ReadBufLen:=BinStream.Read(ReadBuf^,ReadBufSize);
end;
procedure InitReadBuf;
begin
GetMem(ReadBufStart,ReadBufSize);
FillReadBuf;
end;
function ReadChar(var c: char): boolean;
begin
if ReadBufPos>=ReadBufLen then begin
FillReadBuf;
if ReadBufLen=0 then begin
Result:=false;
exit;
end;
end;
c:=ReadBuf^;
inc(ReadBuf);
inc(ReadBufPos);
Result:=true;
end;
procedure InitWriteBuf;
begin
GetMem(WriteBufStart,WriteBufSize);
WriteBuf:=WriteBufStart;
WriteBufPos:=0;
end;
procedure FlushWriteBuf;
begin
if WriteBufPos>0 then begin
ResStream.Write(WriteBufStart^,WriteBufPos);
WriteBuf:=WriteBufStart;
WriteBufPos:=0;
end;
end;
procedure WriteChar(c: char);
begin
WriteBuf^:=c;
inc(WriteBufPos);
inc(WriteBuf);
if WriteBufPos>=WriteBufSize then
FlushWriteBuf;
end;
procedure WriteString(const s: string);
var
i: Integer;
begin
for i:=1 to length(s) do WriteChar(s[i]);
end;
procedure WriteShortString(const s: string);
var
i: Integer;
begin
for i:=1 to length(s) do WriteChar(s[i]);
end;
begin
// fpc is not optimized for building a constant string out of thousands of
// lines. It needs huge amounts of memory and becomes very slow. Therefore big
// files are split into several strings.
InitReadBuf;
InitWriteBuf;
InitByteToStr;
Indent:='';
s:=Indent+'LazarusResources.Add('''+ResourceName+''','''+ResourceType+''',['+LineEnd;
WriteString(s);
Indent:=' '+Indent;
WriteString(Indent);
x:=length(Indent);
RangeString:=false;
CurLine:=1;
RightMargin:=80;
if ReadBufLen>0 then begin
while ReadChar(c) do begin
NewRangeString:=(ord(c)>=32) and (ord(c)<127);
// check if new char fits into line or if a new line must be started
if NewRangeString then begin
if RangeString then
MinCharCount:=2 // char plus '
else
MinCharCount:=3; // ' plus char plus '
if c='''' then inc(MinCharCount);
end else begin
MinCharCount:=1+length(ByteToStr[c]); // # plus number
if RangeString then
inc(MinCharCount); // plus ' for ending last string constant
end;
if x+MinCharCount>RightMargin then begin
// break line
if RangeString then begin
// end string constant
WriteChar('''');
end;
// write line ending
WriteShortString(LineEnd);
x:=0;
inc(CurLine);
// write indention
WriteString(Indent);
inc(x,length(Indent));
// write operator
if (CurLine and 63)<>1 then
WriteChar('+')
else
WriteChar(',');
inc(x);
RangeString:=false;
end;
// write converted byte
if RangeString<>NewRangeString then begin
WriteChar('''');
inc(x);
end;
if NewRangeString then begin
WriteChar(c);
inc(x);
if c='''' then begin
WriteChar(c);
inc(x);
end;
end else begin
WriteChar('#');
inc(x);
WriteShortString(ByteToStr[c]);
inc(x,length(ByteToStr[c]));
end;
// next
RangeString:=NewRangeString;
end;
if RangeString then begin
WriteChar('''');
end;
end else begin
WriteShortString('''''');
end;
Indent:=copy(Indent,3,length(Indent)-2);
s:=LineEnd+Indent+']);'+LineEnd;
WriteString(s);
FlushWriteBuf;
FreeMem(ReadBufStart);
FreeMem(WriteBufStart);
end;
function FindLFMClassName(LFMStream:TStream):ansistring;
{ examples:
object Form1: TForm1
inherited AboutBox2: TAboutBox2
-> the classname is the last word of the first line
}
var c:char;
StartPos, EndPos: Int64;
begin
Result:='';
StartPos:=-1;
c:=' ';
// read till end of line
repeat
// remember last non identifier char position
if (not (c in ['a'..'z','A'..'Z','0'..'9','_'])) then
StartPos:=LFMStream.Position;
if LFMStream.Read(c,1)<>1 then exit;
if LFMStream.Position>1000 then exit;
until c in [#10,#13];
if StartPos<0 then exit;
EndPos:=LFMStream.Position-1;
if EndPos-StartPos>255 then exit;
SetLength(Result,EndPos-StartPos);
LFMStream.Position:=StartPos;
if Length(Result) > 0 then
LFMStream.Read(Result[1],length(Result));
LFMStream.Position:=0;
if not IsValidIdent(Result) then
Result:='';
end;
function LFMtoLRSfile(const LFMfilename: string):boolean;
// returns true if successful
var
LFMFileStream, LRSFileStream: TFileStream;
LFMMemStream, LRSMemStream: TMemoryStream;
LRSfilename: string;
begin
Result:=true;
try
LFMFileStream:=TFileStream.Create(LFMfilename,fmOpenRead);
LFMMemStream:=TMemoryStream.Create;
LRSMemStream:=TMemoryStream.Create;
try
LFMMemStream.SetSize(LFMFileStream.Size);
LFMMemStream.CopyFrom(LFMFileStream,LFMFileStream.Size);
LFMMemStream.Position:=0;
LRSfilename:=ChangeFileExt(LFMfilename,'.lrs');
Result:=LFMtoLRSstream(LFMMemStream,LRSMemStream);
if not Result then exit;
LRSMemStream.Position:=0;
LRSFileStream:=TFileStream.Create(LRSfilename,fmCreate);
try
LRSFileStream.CopyFrom(LRSMemStream,LRSMemStream.Size);
finally
LRSFileStream.Free;
end;
finally
LFMMemStream.Free;
LRSMemStream.Free;
LFMFileStream.Free;
end;
except
on E: Exception do begin
{$IFNDEF DisableChecks}
DebugLn('LFMtoLRSfile ',E.Message);
{$ENDIF}
Result:=false;
end;
end;
end;
function LFMtoLRSstream(LFMStream, LRSStream: TStream):boolean;
// returns true if successful
var FormClassName:ansistring;
BinStream:TMemoryStream;
begin
Result:=true;
try
FormClassName:=FindLFMClassName(LFMStream);
BinStream:=TMemoryStream.Create;
try
LRSObjectTextToBinary(LFMStream,BinStream);
BinStream.Position:=0;
BinaryToLazarusResourceCode(BinStream,LRSStream,FormClassName,'FORMDATA');
finally
BinStream.Free;
end;
except
on E: Exception do begin
{$IFNDEF DisableChecks}
DebugLn('LFMtoLRSstream ',E.Message);
{$ENDIF}
Result:=false;
end;
end;
end;
//==============================================================================
{ TLResourceList }
constructor TLResourceList.Create;
begin
FList := TList.Create;
FMergeList := TList.Create;
FSortedCount := 0;
end;
destructor TLResourceList.Destroy;
var
a: integer;
begin
for a := 0 to FList.Count - 1 do
TLResource(FList[a]).Free;
FList.Free;
FMergeList.Free;
end;
function TLResourceList.Count: integer;
begin
if (Self<>nil) and (FList<>nil) then
Result:=FList.Count
else
Result:=0;
end;
procedure TLResourceList.Add(const Name, ValueType: AnsiString;
const Values: array of string);
var
NewLResource: TLResource;
i, TotalLen, ValueCount, p: integer;
begin
NewLResource := TLResource.Create;
NewLResource.Name := Name;
NewLResource.ValueType := uppercase(ValueType);
ValueCount := High(Values) - Low(Values) + 1;
case ValueCount of
0:
begin
NewLResource.Free;
exit;
end;
1:
NewLResource.Value:=Values[0];
else
TotalLen := 0;
for i := Low(Values) to High(Values) do
inc(TotalLen, length(Values[i]));
SetLength(NewLResource.Value, TotalLen);
p := 1;
for i := Low(Values) to High(Values) do
begin
if length(Values[i]) > 0 then
begin
Move(Values[i][1], NewLResource.Value[p], length(Values[i]));
inc(p, length(Values[i]));
end;
end;
end;
FList.Add(NewLResource);
end;
function TLResourceList.Find(const Name: AnsiString):TLResource;
var
P: Integer;
begin
P := FindPosition(Name);
if P >= 0 then
Result := TLResource(FList[P])
else
Result := nil;
end;
function TLResourceList.Find(const Name, ValueType: AnsiString): TLResource;
var
P, I: Integer;
begin
P := FindPosition(Name);
if P >= 0 then
begin
// Since we can have many resources that have the same name but different type
// we should look before and after found position (do not forget that we are searching
// them by dividing intervals)
// look before position
for I := P - 1 downto 0 do
begin
Result := TLResource(FList[I]);
if SysUtils.CompareText(Result.Name,Name)<>0 then
break;
if Result.ValueType = ValueType then
Exit;
end;
// look behind position
for I := P to FList.Count - 1 do
begin
Result := TLResource(FList[I]);
if SysUtils.CompareText(Result.Name,Name)<>0 then
break;
if Result.ValueType = ValueType then
Exit;
end;
end;
Result := nil;
end;
function TLResourceList.FindPosition(const Name: AnsiString): Integer;
var
L, R, C: Integer;
begin
if FSortedCount < FList.Count then
Sort;
L := 0;
R := FList.Count-1;
while (L <= R) do
begin
Result := (L + R) shr 1;
C := SysUtils.CompareText(Name, TLResource(FList[Result]).Name);
if C < 0 then
R := Result - 1
else
if C > 0 then
L := Result + 1
else
Exit;
end;
Result := -1;
end;
function TLResourceList.GetItems(Index: integer): TLResource;
begin
Result := TLResource(FList[Index]);
end;
procedure TLResourceList.Sort;
{$IFNDEF DisableChecks}
var
i: Integer;
r1: TLResource;
r2: TLResource;
{$ENDIF}
begin
if FSortedCount = FList.Count then
exit;
// sort the unsorted elements
FMergeList.Count := FList.Count;
MergeSort(FList, FMergeList, FSortedCount, FList.Count - 1);
// merge both
Merge(FList, FMergeList, 0, FSortedCount, FList.Count - 1);
FSortedCount := FList.Count;
// check for doubles
{$IFNDEF DisableChecks}
for i:=0 to FList.Count-2 do
begin
r1:=TLResource(FList[i]);
r2:=TLResource(FList[i+1]);
if (SysUtils.CompareText(r1.Name,r2.Name)=0) and (r1.ValueType=r2.ValueType) then
begin
DebugLn(['TLResourceList.Sort ',i,' DUPLICATE RESOURCE FOUND: ',r1.Name,':',r1.ValueType]);
//DumpStack;
end;
end;
{$ENDIF}
end;
procedure TLResourceList.MergeSort(List, MergeList: TList; Pos1, Pos2: integer);
var
cmp, mid: integer;
begin
if Pos1 = Pos2 then
begin
end else
if Pos1 + 1 = Pos2 then
begin
cmp := SysUtils.CompareText(TLResource(List[Pos1]).Name, TLResource(List[Pos2]).Name);
if cmp > 0 then
begin
MergeList[Pos1] := List[Pos1];
List[Pos1] := List[Pos2];
List[Pos2] := MergeList[Pos1];
end;
end else
begin
if Pos2 > Pos1 then
begin
mid := (Pos1 + Pos2) shr 1;
MergeSort(List, MergeList, Pos1, mid);
MergeSort(List, MergeList, mid + 1, Pos2);
Merge(List, MergeList, Pos1, mid + 1, Pos2);
end;
end;
end;
procedure TLResourceList.Merge(List, MergeList: TList; Pos1, Pos2, Pos3: integer);
// merge two sorted arrays
// the first array ranges Pos1..Pos2-1, the second ranges Pos2..Pos3
var
Src1Pos, Src2Pos, DestPos, cmp, a: integer;
begin
if (Pos1 >= Pos2) or (Pos2 > Pos3) then
exit;
Src1Pos := Pos2 - 1;
Src2Pos := Pos3;
DestPos := Pos3;
while (Src2Pos >= Pos2) and (Src1Pos >= Pos1) do
begin
cmp:=SysUtils.CompareText(TLResource(List[Src1Pos]).Name, TLResource(List[Src2Pos]).Name);
if cmp > 0 then
begin
MergeList[DestPos] := List[Src1Pos];
dec(Src1Pos);
end else
begin
MergeList[DestPos] := List[Src2Pos];
dec(Src2Pos);
end;
dec(DestPos);
end;
while Src2Pos >= Pos2 do
begin
MergeList[DestPos] := List[Src2Pos];
dec(Src2Pos);
dec(DestPos);
end;
for a := DestPos + 1 to Pos3 do
List[a] := MergeList[a];
end;
procedure TLResourceList.Add(const Name, ValueType, Value: AnsiString);
begin
Add(Name, ValueType, [Value]);
end;
//------------------------------------------------------------------------------
// Delphi object streams
type
TDelphiValueType = (dvaNull, dvaList, dvaInt8, dvaInt16, dvaInt32, dvaExtended,
dvaString, dvaIdent, dvaFalse, dvaTrue, dvaBinary, dvaSet, dvaLString,
dvaNil, dvaCollection, dvaSingle, dvaCurrency, dvaDate, dvaWString,
dvaInt64, dvaUTF8String);
TDelphiReader = class
private
FStream: TStream;
protected
procedure SkipBytes(Count: Integer);
procedure SkipSetBody;
procedure SkipProperty;
public
constructor Create(Stream: TStream);
procedure ReadSignature;
procedure Read(out Buf; Count: Longint);
function ReadInteger: Longint;
function ReadValue: TDelphiValueType;
function NextValue: TDelphiValueType;
function ReadStr: string;
function EndOfList: Boolean;
procedure SkipValue;
procedure CheckValue(Value: TDelphiValueType);
procedure ReadListEnd;
procedure ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer); virtual;
function ReadFloat: Extended;
function ReadSingle: Single;
function ReadCurrency: Currency;
function ReadDate: TDateTime;
function ReadString: string;
//function ReadWideString: WideString;
function ReadInt64: Int64;
function ReadIdent: string;
end;
TDelphiWriter = class
private
FStream: TStream;
public
constructor Create(Stream: TStream);
procedure Write(const Buf; Count: Longint);
end;
{ TDelphiReader }
procedure ReadError(Msg: string);
begin
raise EReadError.Create(Msg);
end;
procedure PropValueError;
begin
ReadError(rsInvalidPropertyValue);
end;
procedure TDelphiReader.SkipBytes(Count: Integer);
begin
FStream.Position:=FStream.Position+Count;
end;
procedure TDelphiReader.SkipSetBody;
begin
while ReadStr <> '' do ;
end;
procedure TDelphiReader.SkipProperty;
begin
ReadStr; { Skips property name }
SkipValue;
end;
constructor TDelphiReader.Create(Stream: TStream);
begin
FStream:=Stream;
end;
procedure TDelphiReader.ReadSignature;
var
Signature: TFilerSignature;
begin
Signature:='1234';
Read(Signature[1], length(Signature));
if Signature<>FilerSignature then
ReadError(rsInvalidStreamFormat);
end;
procedure TDelphiReader.Read(out Buf; Count: Longint);
begin
FStream.Read(Buf,Count);
end;
function TDelphiReader.ReadInteger: Longint;
var
S: Shortint;
I: Smallint;
begin
case ReadValue of
dvaInt8:
begin
Read(S, SizeOf(Shortint));
Result := S;
end;
dvaInt16:
begin
Read(I, SizeOf(I));
Result := I;
end;
dvaInt32:
Read(Result, SizeOf(Result));
else
Result:=0;
PropValueError;
end;
end;
function TDelphiReader.ReadValue: TDelphiValueType;
var b: byte;
begin
Read(b,1);
Result:=TDelphiValueType(b);
end;
function TDelphiReader.NextValue: TDelphiValueType;
begin
Result := ReadValue;
FStream.Position:=FStream.Position-1;
end;
function TDelphiReader.ReadStr: string;
var
L: Byte;
begin
Read(L, SizeOf(Byte));
SetLength(Result, L);
if L>0 then
Read(Result[1], L);
end;
function TDelphiReader.EndOfList: Boolean;
begin
Result := (ReadValue = dvaNull);
FStream.Position:=FStream.Position-1;
end;
procedure TDelphiReader.SkipValue;
procedure SkipList;
begin
while not EndOfList do SkipValue;
ReadListEnd;
end;
procedure SkipBinary(BytesPerUnit: Integer);
var
Count: Longint;
begin
Read(Count, SizeOf(Count));
SkipBytes(Count * BytesPerUnit);
end;
procedure SkipCollection;
begin
while not EndOfList do
begin
if NextValue in [dvaInt8, dvaInt16, dvaInt32] then SkipValue;
SkipBytes(1);
while not EndOfList do SkipProperty;
ReadListEnd;
end;
ReadListEnd;
end;
begin
case ReadValue of
dvaNull: { no value field, just an identifier };
dvaList: SkipList;
dvaInt8: SkipBytes(SizeOf(Byte));
dvaInt16: SkipBytes(SizeOf(Word));
dvaInt32: SkipBytes(SizeOf(LongInt));
dvaExtended: SkipBytes(SizeOf(Extended));
dvaString, dvaIdent: ReadStr;
dvaFalse, dvaTrue: { no value field, just an identifier };
dvaBinary: SkipBinary(1);
dvaSet: SkipSetBody;
dvaLString: SkipBinary(1);
dvaCollection: SkipCollection;
dvaSingle: SkipBytes(Sizeof(Single));
dvaCurrency: SkipBytes(SizeOf(Currency));
dvaDate: SkipBytes(Sizeof(TDateTime));
dvaWString: SkipBinary(Sizeof(WideChar));
dvaInt64: SkipBytes(Sizeof(Int64));
dvaUTF8String: SkipBinary(1);
end;
end;
procedure TDelphiReader.CheckValue(Value: TDelphiValueType);
begin
if ReadValue <> Value then
begin
FStream.Position:=FStream.Position-1;
SkipValue;
PropValueError;
end;
end;
procedure TDelphiReader.ReadListEnd;
begin
CheckValue(dvaNull);
end;
procedure TDelphiReader.ReadPrefix(var Flags: TFilerFlags;
var AChildPos: Integer);
var
Prefix: Byte;
begin
Flags := [];
if Byte(NextValue) and $F0 = $F0 then
begin
Prefix := Byte(ReadValue);
if (Prefix and ObjStreamMaskInherited)>0 then
Include(Flags,ffInherited);
if (Prefix and ObjStreamMaskChildPos)>0 then
Include(Flags,ffChildPos);
if (Prefix and ObjStreamMaskInline)>0 then
Include(Flags,ffInline);
if ffChildPos in Flags then AChildPos := ReadInteger;
end;
end;
function TDelphiReader.ReadFloat: Extended;
begin
if ReadValue = dvaExtended then
Read(Result, SizeOf(Result))
else begin
FStream.Position:=FStream.Position-1;
Result := ReadInteger;
end;
end;
function TDelphiReader.ReadSingle: Single;
begin
if ReadValue = dvaSingle then
Read(Result, SizeOf(Result))
else begin
FStream.Position:=FStream.Position-1;
Result := ReadInteger;
end;
end;
function TDelphiReader.ReadCurrency: Currency;
begin
if ReadValue = dvaCurrency then
Read(Result, SizeOf(Result))
else begin
FStream.Position:=FStream.Position-1;
Result := ReadInteger;
end;
end;
function TDelphiReader.ReadDate: TDateTime;
begin
if ReadValue = dvaDate then
Read(Result, SizeOf(Result))
else begin
FStream.Position:=FStream.Position-1;
Result := ReadInteger;
end;
end;
function TDelphiReader.ReadString: string;
var
L: Integer;
begin
Result := '';
if NextValue in [dvaWString, dvaUTF8String] then begin
ReadError('TDelphiReader.ReadString: WideString and UTF8String are not implemented yet');
//Result := ReadWideString;
end else
begin
L := 0;
case ReadValue of
dvaString:
Read(L, SizeOf(Byte));
dvaLString:
Read(L, SizeOf(Integer));
else
PropValueError;
end;
SetLength(Result, L);
Read(Pointer(Result)^, L);
end;
end;
function TDelphiReader.ReadInt64: Int64;
begin
if NextValue = dvaInt64 then
begin
ReadValue;
Read(Result, Sizeof(Result));
end
else
Result := ReadInteger;
end;
function TDelphiReader.ReadIdent: string;
var
L: Byte;
begin
case ReadValue of
dvaIdent:
begin
Read(L, SizeOf(Byte));
SetLength(Result, L);
Read(Result[1], L);
end;
dvaFalse:
Result := 'False';
dvaTrue:
Result := 'True';
dvaNil:
Result := 'nil';
dvaNull:
Result := 'Null';
else
Result:='';
PropValueError;
end;
end;
{ TDelphiWriter }
{ MultiByte Character Set (MBCS) byte type }
type
TMbcsByteType = (mbSingleByte, mbLeadByte, mbTrailByte);
function ByteType(const S: string; Index: Integer): TMbcsByteType;
begin
Result := mbSingleByte;
{ ToDo:
if SysLocale.FarEast then
Result := ByteTypeTest(PChar(S), Index-1);
}
end;
constructor TDelphiWriter.Create(Stream: TStream);
begin
FStream:=Stream;
end;
procedure TDelphiWriter.Write(const Buf; Count: Longint);
begin
FStream.Write(Buf,Count);
end;
procedure ReadLFMHeader(LFMStream: TStream;
out LFMType, LFMComponentName, LFMClassName: String);
var
c:char;
Token: String;
begin
{ examples:
object Form1: TForm1
inherited AboutBox2: ns.unit2/TAboutBox2
}
LFMComponentName:='';
LFMClassName := '';
LFMType := '';
Token := '';
while (LFMStream.Read(c,1)=1) and (LFMStream.Position<1000) do begin
case c of
' ',#9,':':
begin
if Token<>'' then begin
if LFMType = '' then
LFMType := Token
else if LFMComponentName='' then
LFMComponentName:=Token
else if LFMClassName = '' then
begin
LFMClassName := Token;
exit;
end;
Token := '';
end;
end;
'a'..'z','A'..'Z','0'..'9','_','.','/':
Token:=Token+c;
else
break;
end;
end;
LFMStream.Position:=0;
end;
procedure ReadLFMHeader(const LFMSource: string;
out LFMClassName: String; out LFMType: String);
var
LFMComponentName: string;
begin
ReadLFMHeader(LFMSource,LFMType,LFMComponentName,LFMClassName);
end;
procedure ReadLFMHeader(const LFMSource: string; out LFMType, LFMComponentName,
LFMClassName: String);
var
p: Integer;
StartPos: LongInt;
begin
{ examples:
object Form1: TForm1
inherited AboutBox2: ns.unit1/TAboutBox2
- LFMType is the first word on the line, e.g. object or inherited
- LFMComponentName is the second word
- LFMClassName is the third
}
// read LFMType
p:=1;
while (p<=length(LFMSource))
and (LFMSource[p] in ['a'..'z','A'..'Z','0'..'9','_']) do
inc(p);
LFMType:=copy(LFMSource,1,p-1);
// read LFMComponentName
while (p<=length(LFMSource)) and (LFMSource[p] in [' ',#9]) do inc(p);
StartPos:=p;
while (p<=length(LFMSource))
and (LFMSource[p] in ['a'..'z','A'..'Z','0'..'9','_']) do
inc(p);
LFMComponentName:=copy(LFMSource,StartPos,p-StartPos);
// read LFMClassName
while (p<=length(LFMSource)) and (LFMSource[p] in [' ',#9,':']) do inc(p);
StartPos:=p;
while (p<=length(LFMSource))
and (LFMSource[p] in ['a'..'z','A'..'Z','0'..'9','_','.','/']) do
inc(p);
LFMClassName:=copy(LFMSource,StartPos,p-StartPos);
end;
function ReadLFMHeaderFromFile(const Filename: string; out LFMType,
LFMComponentName, LFMClassName: String): boolean;
var
fs: TFileStream;
Header: string;
Cnt: LongInt;
begin
Result:=false;
try
fs:=TFileStream.Create(Filename,fmOpenRead);
try
SetLength(Header,600);
Cnt:=fs.Read(Header[1],length(Header));
SetLength(Header,Cnt);
ReadLFMHeader(Header,LFMType,LFMComponentName,LFMClassName);
Result:=LFMClassName<>'';
finally
fs.Free;
end;
except
end;
end;
function CreateLFMFile(AComponent: TComponent; LFMStream: TStream): integer;
// 0 = ok
// -1 = error while streaming AForm to binary stream
// -2 = error while streaming binary stream to text file
var
BinStream: TMemoryStream;
DestroyDriver: Boolean;
Writer: TWriter;
begin
Result:=0;
BinStream:=TMemoryStream.Create;
try
try
// write component to binary stream
DestroyDriver:=false;
Writer:=CreateLRSWriter(BinStream,DestroyDriver);
try
Writer.WriteDescendent(AComponent,nil);
finally
if DestroyDriver then Writer.Driver.Free;
Writer.Free;
end;
except
Result:=-1;
exit;
end;
try
// transform binary to text
BinStream.Position:=0;
LRSObjectBinaryToText(BinStream,LFMStream);
except
Result:=-2;
exit;
end;
finally
BinStream.Free;
end;
end;
function SameLFMTypeName(aUnitname, aTypename, LFMTypename: string): boolean;
var
p: SizeInt;
begin
p:=Pos('/',LFMTypename);
if p>0 then
begin
if aUnitname<>'' then
Result:=CompareText(aUnitname+'/'+aTypename,LFMTypename)=0
else
Result:=CompareText(aTypename,copy(LFMTypename,p+1,length(LFMTypename)))=0;
end else begin
Result:=CompareText(aTypename,LFMTypename)=0;
end;
end;
procedure LRSObjectBinaryToText(Input, Output: TStream);
procedure OutStr(const s: String);
{$IFDEF VerboseLRSObjectBinaryToText}
var
i: Integer;
{$ENDIF}
begin
{$IFDEF VerboseLRSObjectBinaryToText}
for i:=1 to length(s) do begin
if (s[i] in [#0..#8,#11..#12,#14..#31]) then begin
DbgOut('#'+IntToStr(ord(s[i])));
RaiseGDBException('ObjectLRSToText: Invalid character');
end else
DbgOut(s[i]);
end;
{$ENDIF}
if Length(s) > 0 then
Output.Write(s[1], Length(s));
end;
procedure OutLn(const s: String);
begin
OutStr(s + LineEnding);
end;
procedure OutString(const s: String);
var
res, NewStr: String;
i: Integer;
InString, NewInString: Boolean;
begin
if s<>'' then begin
res := '';
InString := False;
for i := 1 to Length(s) do begin
NewInString := InString;
case s[i] of
#0..#31: begin
NewInString := False;
NewStr := '#' + IntToStr(Ord(s[i]));
end;
'''': begin
NewInString := True;
NewStr:=''''''; // write two ticks, so the reader will read one
end;
else begin
NewInString := True;
NewStr := s[i];
end;
end;
if NewInString <> InString then begin
NewStr := '''' + NewStr;
InString := NewInString;
end;
res := res + NewStr;
end;
if InString then res := res + '''';
end else begin
res:='''''';
end;
OutStr(res);
end;
procedure OutWideString(const s: WideString);
// write as normal string
var
res, NewStr: String;
i: Integer;
InString, NewInString: Boolean;
begin
//debugln('OutWideString ',s);
res := '';
if s<>'' then begin
InString := False;
for i := 1 to Length(s) do begin
NewInString := InString;
if (ord(s[i])<ord(' ')) or (ord(s[i])>=127) then begin
// special char
NewInString := False;
NewStr := '#' + IntToStr(Ord(s[i]));
end
else if s[i]='''' then begin
// '
if InString then
NewStr := ''''''
else
NewStr := '''''''';
end
else begin
// normal char
NewInString := True;
NewStr := AnsiString(s[i]);
end;
if NewInString <> InString then begin
NewStr := '''' + NewStr;
InString := NewInString;
end;
res := res + NewStr;
end;
if InString then res := res + '''';
end else begin
res:='''''';
end;
OutStr(res);
end;
function ReadInt(ValueType: TValueType): LongInt;
var
w: Word;
begin
case ValueType of
vaInt8: Result := ShortInt(Input.ReadByte);
vaInt16: begin
w:=ReadLRSWord(Input);
//DebugLn('ReadInt vaInt16 w=',IntToStr(w));
Result := SmallInt(w);
end;
vaInt32: Result := ReadLRSInteger(Input);
else Result := 0;
end;
end;
function ReadInt: LongInt;
begin
Result := ReadInt(TValueType(Input.ReadByte));
end;
function ReadShortString: String;
var
len: Byte;
begin
len := Input.ReadByte;
SetLength(Result, len);
if (Len > 0) then
Input.Read(Result[1], len);
end;
function ReadLongString: String;
var
len: integer;
begin
len := ReadLRSInteger(Input);
SetLength(Result, len);
if (Len > 0) then
Input.Read(Result[1], len);
end;
procedure ReadPropList(const indent: String);
procedure ProcessValue(ValueType: TValueType; const Indent: String);
procedure Stop(const s: String);
begin
RaiseGDBException('ObjectLRSToText '+s);
end;
function ValueTypeAsString(ValueType: TValueType): string;
begin
case ValueType of
vaNull: Result:='vaNull';
vaList: Result:='vaList';
vaInt8: Result:='vaInt8';
vaInt16: Result:='vaInt16';
vaInt32: Result:='vaInt32';
vaExtended: Result:='vaExtended';
vaString: Result:='vaString';
vaIdent: Result:='vaIdent';
vaFalse: Result:='vaFalse';
vaTrue: Result:='vaTrue';
vaBinary: Result:='vaBinary';
vaSet: Result:='vaSet';
vaLString: Result:='vaLString';
vaNil: Result:='vaNil';
vaCollection: Result:='vaCollection';
vaSingle: Result:='vaSingle';
vaCurrency: Result:='vaCurrency';
vaDate: Result:='vaDate';
vaWString: Result:='vaWString';
vaInt64: Result:='vaInt64';
vaUTF8String: Result:='vaUTF8String';
vaUString: Result:='vaUString';
vaQWord : Result:='vaQWord';
else Result:='Unknown ValueType='+dbgs(Ord(ValueType));
end;
end;
procedure UnknownValueType;
var
s: String;
{$IFNDEF DisableChecks}
HintStr: string;
HintLen: Int64;
{$ENDIF}
begin
s:=ValueTypeAsString(ValueType);
if s<>'' then
s:='Unimplemented ValueType='+s;
{$IFNDEF DisableChecks}
HintLen:=Output.Position;
if HintLen>50 then HintLen:=50;
SetLength(HintStr,HintLen);
if HintStr<>'' then begin
try
Output.Position:=Output.Position-length(HintStr);
Output.Read(HintStr[1],length(HintStr));
//debugln('ObjectLRSToText:');
debugln(DbgStr(HintStr));
except
end;
end;
{$ENDIF}
s:=s+' ';
Stop(s);
end;
procedure ProcessBinary;
var
ToDo, DoNow, StartPos, i: LongInt;
lbuf: array[0..31] of Byte;
s: String;
p: pchar;
const
HexDigits: array[0..$F] of char = '0123456789ABCDEF';
begin
ToDo := ReadLRSCardinal(Input);
OutLn('{');
while ToDo > 0 do begin
DoNow := ToDo;
if DoNow > 32 then DoNow := 32;
Dec(ToDo, DoNow);
s := Indent + ' ';
StartPos := length(s);
Input.Read(lbuf, DoNow);
setlength(s, StartPos+DoNow*2);
p := @s[StartPos];
for i := 0 to DoNow - 1 do begin
inc(p);
p^ := HexDigits[(lbuf[i] shr 4) and $F];
inc(p);
p^ := HexDigits[lbuf[i] and $F];
end;
OutLn(s);
end;
OutStr(indent);
OutLn('}');
end;
var
s: String;
IsFirst: Boolean;
ext: Extended;
ASingle: single;
ADate: TDateTime;
ACurrency: Currency;
AWideString: WideString;
begin
//DebugLn(['ProcessValue ',Indent,' ValueType="',ValueTypeAsString(ValueType),'"']);
case ValueType of
vaList: begin
OutStr('(');
IsFirst := True;
while True do begin
ValueType := TValueType(Input.ReadByte);
if ValueType = vaNull then break;
if IsFirst then begin
OutLn('');
IsFirst := False;
end;
OutStr(Indent + ' ');
ProcessValue(ValueType, Indent + ' ');
end;
OutLn(Indent + ')');
end;
vaInt8: begin
// MG: IntToStr has a bug with ShortInt, therefore these typecasts
OutLn(IntToStr(Integer(ShortInt(Input.ReadByte))));
end;
vaInt16: OutLn(IntToStr(SmallInt(ReadLRSWord(Input))));
vaInt32: OutLn(IntToStr(ReadLRSInteger(Input)));
vaInt64: OutLn(IntToStr(ReadLRSInt64(Input)));
vaExtended: begin
ext:=ReadLRSExtended(Input);
OutLn(FloatToStr(ext));
end;
vaString: begin
OutString(ReadShortString);
OutLn('');
end;
vaIdent: OutLn(ReadShortString);
vaFalse: OutLn('False');
vaTrue: OutLn('True');
vaBinary: ProcessBinary;
vaSet: begin
OutStr('[');
IsFirst := True;
while True do begin
s := ReadShortString;
if Length(s) = 0 then break;
if not IsFirst then OutStr(', ');
IsFirst := False;
OutStr(s);
end;
OutLn(']');
end;
vaLString: begin
OutString(ReadLongString);
OutLn('');
end;
vaNil:
OutLn('nil');
vaCollection: begin
OutStr('<');
while Input.ReadByte <> 0 do begin
OutLn(Indent);
Input.Seek(-1, soFromCurrent);
OutStr(indent + ' item');
ValueType := TValueType(Input.ReadByte);
if ValueType <> vaList then
OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
OutLn('');
ReadPropList(indent + ' ');
OutStr(indent + ' end');
end;
OutLn('>');
end;
vaSingle: begin
ASingle:=ReadLRSSingle(Input);
OutLn(FloatToStr(ASingle) + 's');
end;
vaDate: begin
ADate:=TDateTime(ReadLRSDouble(Input));
OutLn(FloatToStr(ADate) + 'd');
end;
vaCurrency: begin
ACurrency:=ReadLRSCurrency(Input);
OutLn(FloatToStr(ACurrency * 10000) + 'c');
end;
vaWString,vaUString: begin
AWideString:=ReadLRSWideString(Input);
OutWideString(AWideString);
OutLn('');
end;
else
if ord(ValueType)=20 then begin
// vaUTF8String
// Delphi saves widestrings as UTF8 strings
// The LCL does not use widestrings, but UTF8 directly
// so, simply read and write the string
OutString(ReadLongString);
OutLn('');
end else
UnknownValueType;
end;
end;
var
NextByte: Byte;
begin
while Input.ReadByte <> 0 do begin
Input.Seek(-1, soFromCurrent);
OutStr(indent + ReadShortString + ' = ');
NextByte:=Input.ReadByte;
if NextByte<>0 then
ProcessValue(TValueType(NextByte), Indent)
else
OutLn('');
end;
end;
procedure ReadObject(const indent: String);
var
b: Byte;
ObjClassName, ObjName: String;
ChildPos: LongInt;
begin
ChildPos := 0;
// Check for FilerFlags
b := Input.ReadByte;
if (b and $f0) = $f0 then begin
if (b and ObjStreamMaskChildPos) <> 0 then
ChildPos := ReadInt;
end else begin
b := 0;
Input.Seek(-1, soFromCurrent);
end;
ObjClassName := ReadShortString;
ObjName := ReadShortString;
OutStr(Indent);
if (b and ObjStreamMaskInherited) <> 0 then OutStr('inherited')
else if (b and ObjStreamMaskInline) <> 0 then OutStr('inline')
else OutStr('object');
OutStr(' ');
if ObjName <> '' then
OutStr(ObjName + ': ');
OutStr(ObjClassName);
if (b and ObjStreamMaskChildPos) <> 0 then OutStr('[' + IntToStr(ChildPos) + ']');
OutLn('');
ReadPropList(indent + ' ');
while Input.ReadByte <> 0 do begin
Input.Seek(-1, soFromCurrent);
ReadObject(indent + ' ');
end;
OutLn(indent + 'end');
end;
var
OldDecimalSeparator: Char;
OldThousandSeparator: Char;
Signature: TFilerSignature;
begin
// Endian note: comparing 2 cardinals is endian independent
Signature:='1234';
Input.Read(Signature[1], length(Signature));
if Signature<>FilerSignature then
raise EReadError.Create('Illegal stream image' {###SInvalidImage});
OldDecimalSeparator:=DefaultFormatSettings.DecimalSeparator;
DefaultFormatSettings.DecimalSeparator:='.';
OldThousandSeparator:=DefaultFormatSettings.ThousandSeparator;
DefaultFormatSettings.ThousandSeparator:=',';
try
ReadObject('');
finally
DefaultFormatSettings.DecimalSeparator:=OldDecimalSeparator;
DefaultFormatSettings.ThousandSeparator:=OldThousandSeparator;
end;
end;
function TestFormStreamFormat(Stream: TStream): TLRSStreamOriginalFormat;
var
Pos: TStreamSeekType;
Signature: TFilerSignature;
begin
Pos := Stream.Position;
Signature[1] := #0; // initialize, in case the stream is at its end
Stream.Read(Signature, length(Signature));
Stream.Position := Pos;
if (Signature[1] = #$FF) or (Signature = FilerSignature) then
Result := sofBinary
// text format may begin with "object", "inherited", or whitespace
else if Signature[1] in ['o','O','i','I',' ',#13,#11,#9] then
Result := sofText
else
Result := sofUnknown;
end;
type
TObjectTextConvertProc = procedure (Input, Output: TStream);
procedure InternalLRSBinaryToText(Input, Output: TStream;
var OriginalFormat: TLRSStreamOriginalFormat;
ConvertProc: TObjectTextConvertProc;
BinarySignature: TFilerSignature);
var
Pos: TStreamSeekType;
Signature: TFilerSignature;
begin
Pos := Input.Position;
Signature := BinarySignature;
Signature[1]:=#0;
Input.Read(Signature[1], length(Signature));
Input.Position := Pos;
if Signature = BinarySignature then
begin // definitely binary format
if OriginalFormat = sofBinary then begin
if Output is TMemoryStream then
TMemoryStream(Output).SetSize(Output.Position+(Input.Size-Input.Position));
Output.CopyFrom(Input, Input.Size - Input.Position)
end else
begin
if OriginalFormat = sofUnknown then
Originalformat := sofBinary;
ConvertProc(Input, Output);
end;
end
else // might be text format
begin
if OriginalFormat = sofBinary then
ConvertProc(Input, Output)
else
begin
if OriginalFormat = sofUnknown then
begin // text format may begin with "object", "inherited", or whitespace
if Signature[1] in ['o','O','i','I',' ',#13,#11,#9] then
OriginalFormat := sofText
else // not binary, not text... let it raise the exception
begin
ConvertProc(Input, Output);
Exit;
end;
end;
if OriginalFormat = sofText then begin
if Output is TMemoryStream then
TMemoryStream(Output).SetSize(Output.Position
+(Input.Size - Input.Position));
Output.CopyFrom(Input, Input.Size - Input.Position);
end;
end;
end;
end;
procedure LRSObjectTextToBinary(Input, Output: TStream; Links: TLRPositionLinks);
var
parser: TParser;
OldDecimalSeparator: Char;
OldThousandSeparator: Char;
TokenStartPos: LongInt;
procedure WriteShortString(const s: String);
var
Size: Integer;
begin
Size:=length(s);
if Size>255 then Size:=255;
Output.WriteByte(byte(Size));
if Size > 0 then
Output.Write(s[1], Size);
end;
procedure WriteLongString(const s: String);
begin
WriteLRSInteger(Output,Length(s));
if Length(s) > 0 then
Output.Write(s[1], Length(s));
end;
procedure WriteWideString(const s: WideString);
begin
WriteLRSInteger(Output,Length(s));
if Length(s) > 0 then
Output.Write(s[1], Length(s)*2);
end;
procedure WriteInteger(value: LongInt);
begin
if (value >= -128) and (value <= 127) then begin
Output.WriteByte(Ord(vaInt8));
Output.WriteByte(Byte(value));
end else if (value >= -32768) and (value <= 32767) then begin
Output.WriteByte(Ord(vaInt16));
WriteLRSWord(Output,Word(value));
end else begin
Output.WriteByte(ord(vaInt32));
WriteLRSInteger(Output,value);
end;
end;
procedure WriteInt64(const Value: Int64);
begin
if (Value >= -$80000000) and (Value <= $7fffffff) then
WriteInteger(Integer(Value))
else begin
Output.WriteByte(ord(vaInt64));
WriteLRSInt64(Output,Value);
end;
end;
procedure WriteIntegerStr(const s: string);
begin
if length(s)>7 then
WriteInt64(StrToInt64(s))
else
WriteInteger(StrToInt(s));
end;
function ParserNextToken: Char;
begin
TokenStartPos:=Parser.SourcePos;
Result:=Parser.NextToken;
if Links<>nil then
Links.SetPosition(TokenStartPos,Parser.SourcePos,Output.Position,true);
end;
procedure ProcessProperty; forward;
{$if not declared(toWString)}
const toWString = char(5);
{$endif}
procedure ProcessValue;
procedure RaiseValueExpected;
begin
parser.Error('Value expected, but '+parser.TokenString+' found');
end;
var
flt: Extended;
stream: TMemoryStream;
BinDataSize: LongInt;
toStringBuf: String;
begin
if parser.TokenSymbolIs('END') then exit;
if parser.TokenSymbolIs('OBJECT') then
RaiseValueExpected;
case parser.Token of
toInteger:
begin
WriteIntegerStr(parser.TokenString);
ParserNextToken;
end;
toFloat:
begin
flt := Parser.TokenFloat;
case parser.FloatType of
's': begin
Output.WriteByte(Ord(vaSingle));
WriteLRSSingle(Output,flt);
end;
'd': begin
Output.WriteByte(Ord(vaDate));
WriteLRSDouble(Output,flt);
end;
'c': begin
Output.WriteByte(Ord(vaCurrency));
WriteLRSCurrency(Output,flt/10000);
end;
else
begin
Output.WriteByte(Ord(vaExtended));
WriteLRSExtended(Output,flt);
end;
end;
ParserNextToken;
end;
toString:
begin
toStringBuf := parser.TokenString;
//DebugLn(['ProcessValue toStringBuf="',toStringBuf,'" ',dbgstr(toStringBuf)]);
while ParserNextToken = '+' do
begin
ParserNextToken; // Get next string fragment
if not (parser.Token in [toString,toWString]) then
parser.CheckToken(toString);
toStringBuf := toStringBuf + parser.TokenString;
end;
if length(toStringBuf)<256 then begin
//debugln('LRSObjectTextToBinary.ProcessValue WriteShortString');
Output.WriteByte(Ord(vaString));
WriteShortString(toStringBuf);
end else begin
//debugln('LRSObjectTextToBinary.ProcessValue WriteLongString');
Output.WriteByte(Ord(vaLString));
WriteLongString(toStringBuf);
end;
end;
toWString:
begin
toStringBuf := parser.TokenString;
//DebugLn(['ProcessValue toStringBuf="',toStringBuf,'" ',dbgstr(toStringBuf)]);
while ParserNextToken = '+' do
begin
ParserNextToken; // Get next string fragment
if not (parser.Token in [toString,toWString]) then
parser.CheckToken(toString);
toStringBuf := toStringBuf + parser.TokenString;
end;
Output.WriteByte(Ord(vaWString));
WriteWideString(UTF8Decode(toStringBuf));
end;
toSymbol:
begin
if CompareText(parser.TokenString, 'True') = 0 then
Output.WriteByte(Ord(vaTrue))
else if CompareText(parser.TokenString, 'False') = 0 then
Output.WriteByte(Ord(vaFalse))
else if CompareText(parser.TokenString, 'nil') = 0 then
Output.WriteByte(Ord(vaNil))
else
begin
Output.WriteByte(Ord(vaIdent));
WriteShortString(parser.TokenComponentIdent);
end;
ParserNextToken;
end;
// Set
'[':
begin
ParserNextToken;
Output.WriteByte(Ord(vaSet));
if parser.Token <> ']' then
while True do
begin
parser.CheckToken(toSymbol);
WriteShortString(parser.TokenString);
ParserNextToken;
if parser.Token = ']' then
break;
parser.CheckToken(',');
ParserNextToken;
end;
Output.WriteByte(0);
ParserNextToken;
end;
// List
'(':
begin
Output.WriteByte(Ord(vaList));
ParserNextToken;
while parser.Token <> ')' do
ProcessValue;
Output.WriteByte(0);
ParserNextToken;
end;
// Collection
'<':
begin
ParserNextToken;
Output.WriteByte(Ord(vaCollection));
while parser.Token <> '>' do
begin
parser.CheckTokenSymbol('item');
ParserNextToken;
// ConvertOrder
Output.WriteByte(Ord(vaList));
while not parser.TokenSymbolIs('end') do
ProcessProperty;
ParserNextToken; // Skip 'end'
Output.WriteByte(0);
end;
Output.WriteByte(0);
ParserNextToken;
end;
// Binary data
'{':
begin
Output.WriteByte(Ord(vaBinary));
stream := TMemoryStream.Create;
try
parser.HexToBinary(stream);
BinDataSize:=integer(stream.Size);
WriteLRSInteger(Output,BinDataSize);
Output.Write(Stream.Memory^, BinDataSize);
Stream.Position:=0;
//debugln('LRSObjectTextToBinary binary data "',dbgMemStream(Stream,30),'"');
finally
stream.Free;
end;
ParserNextToken;
end;
else
parser.Error('Invalid Property');
end;
end;
procedure ProcessProperty;
var
name: String;
begin
// Get name of property
parser.CheckToken(toSymbol);
name := parser.TokenString;
while True do begin
ParserNextToken;
if parser.Token <> '.' then break;
ParserNextToken;
parser.CheckToken(toSymbol);
name := name + '.' + parser.TokenString;
end;
WriteShortString(name);
parser.CheckToken('=');
ParserNextToken;
ProcessValue;
end;
procedure ProcessObject;
var
Flags: Byte;
ChildPos: Integer;
ObjectName, ObjectType: String;
begin
if parser.TokenSymbolIs('OBJECT') then
Flags :=0 { IsInherited := False }
else if parser.TokenSymbolIs('INHERITED') then
Flags := 1 { IsInherited := True; }
else begin
parser.CheckTokenSymbol('INLINE');
Flags := 4;
end;
ParserNextToken;
parser.CheckToken(toSymbol);
if parser.TokenSymbolIs('END') then begin
// 'object end': no name, no content
// this is normally invalid, but Delphi can create this, so ignore it
exit;
end;
ObjectName := '';
ObjectType := parser.TokenString;
ParserNextToken;
ChildPos := 0;
if parser.Token = ':' then begin
ParserNextToken;
parser.CheckToken(toSymbol);
ObjectName := ObjectType;
ObjectType := parser.TokenString;
ParserNextToken;
if parser.Token = '[' then begin
ParserNextToken;
ChildPos := parser.TokenInt;
ParserNextToken;
parser.CheckToken(']');
ParserNextToken;
Flags := Flags or 2;
end;
end;
if Flags <> 0 then begin
Output.WriteByte($f0 or Flags);
if (Flags and ObjStreamMaskChildPos) <> 0 then
WriteInteger(ChildPos);
end;
WriteShortString(ObjectType);
WriteShortString(ObjectName);
// Convert property list
while not (parser.TokenSymbolIs('END') or
parser.TokenSymbolIs('OBJECT') or
parser.TokenSymbolIs('INHERITED') or
parser.TokenSymbolIs('INLINE'))
do
ProcessProperty;
Output.WriteByte(0); // Terminate property list
// Convert child objects
while not parser.TokenSymbolIs('END') do ProcessObject;
ParserNextToken; // Skip end token
Output.WriteByte(0); // Terminate property list
end;
var
Count: Integer;
begin
if Links<>nil then begin
// sort links for LFM positions
Links.Sort(true);
end;
parser := TParser.Create(Input);
OldDecimalSeparator:=DefaultFormatSettings.DecimalSeparator;
DefaultFormatSettings.DecimalSeparator:='.';
OldThousandSeparator:=DefaultFormatSettings.ThousandSeparator;
DefaultFormatSettings.ThousandSeparator:=',';
try
Count:=0;
repeat
Output.Write(FilerSignature[1], length(FilerSignature));
ProcessObject;
inc(Count);
until parser.TokenString='';
if Count>1 then
Output.WriteByte(0); // Terminate object list
finally
parser.Free;
DefaultFormatSettings.DecimalSeparator:=OldDecimalSeparator;
DefaultFormatSettings.ThousandSeparator:=OldThousandSeparator;
end;
end;
procedure LRSObjectToText(Input, Output: TStream;
var OriginalFormat: TLRSStreamOriginalFormat);
begin
InternalLRSBinaryToText(Input, Output, OriginalFormat,
@LRSObjectBinaryToText, FilerSignature);
end;
procedure LRSObjectResToText(Input, Output: TStream;
var OriginalFormat: TLRSStreamOriginalFormat);
begin
InternalLRSBinaryToText(Input, Output, OriginalFormat,
@LRSObjectResourceToText, #255);
end;
procedure LRSObjectResourceToText(Input, Output: TStream);
begin
Input.ReadResHeader;
LRSObjectBinaryToText(Input, Output);
end;
procedure FormDataToText(FormStream, TextStream: TStream; aFormat: TLRSStreamOriginalFormat);
begin
if aFormat = sofUnknown then
aFormat := TestFormStreamFormat(FormStream);
case aFormat of
sofBinary:
LRSObjectResourceToText(FormStream, TextStream);
sofText:
begin
if TextStream is TMemoryStream then
TMemoryStream(TextStream).SetSize(TextStream.Position+FormStream.Size);
TextStream.CopyFrom(FormStream,FormStream.Size);
end;
else
raise Exception.Create(rsInvalidFormObjectStream);
end;
end;
function InitLazResourceComponent(Instance: TComponent;
RootAncestor: TClass): Boolean;
function InitComponent(ClassType: TClass): Boolean;
var
{$ifdef UseLRS}
LazResource: TLResource;
{$endif}
{$ifdef UseRES}
FPResource: TFPResourceHandle;
{$endif}
ResName: String;
GenericInd: Integer;
Stream: TStream;
Reader: TReader;
DestroyDriver: Boolean;
Driver: TAbstractObjectReader;
begin
//DebugLn(['[InitComponent] ClassType=',ClassType.Classname,' Instance=',DbgsName(Instance),' RootAncestor=',DbgsName(RootAncestor),' ClassType.ClassParent=',DbgsName(ClassType.ClassParent)]);
Result := False;
if (ClassType = TComponent) or (ClassType = RootAncestor) then
Exit;
if Assigned(ClassType.ClassParent) then
Result := InitComponent(ClassType.ClassParent);
Stream := nil;
ResName := ClassType.ClassName;
// Generics class name can contain <> and resource files do not support it
GenericInd := ResName.IndexOf('<');
if GenericInd > 0 then
SetLength(ResName, GenericInd);
{$ifdef UseLRS}
LazResource := LazarusResources.Find(ResName);
if (LazResource <> nil) and (LazResource.Value <> '') then
Stream := TLazarusResourceStream.CreateFromHandle(LazResource);
//DebugLn('[InitComponent] CompResource found for ',ClassType.Classname);
{$endif}
{$ifdef UseRES}
if Stream = nil then
begin
FPResource := FindResourceLFM(ResName);
if FPResource <> 0 then
Stream := TLazarusResourceStream.CreateFromHandle(HInstance, FPResource);
end;
{$endif}
if Stream = nil then
Exit;
try
//DebugLn('Form Stream "',ClassType.ClassName,'"');
//try
DestroyDriver:=false;
Reader := CreateLRSReader(Stream, DestroyDriver);
try
Reader.ReadRootComponent(Instance);
finally
Driver := Reader.Driver;
Reader.Free;
if DestroyDriver then
Driver.Free;
end;
//except
// on E: Exception do begin
// DebugLn(Format(rsFormStreamingError,[ClassType.ClassName,E.Message]));
// exit;
// end;
//end;
finally
Stream.Free;
end;
Result := True;
end;
begin
if Instance.ComponentState * [csLoading, csInline] <> []
then begin
// global loading not needed
Result := InitComponent(Instance.ClassType);
end
else try
BeginGlobalLoading;
Result := InitComponent(Instance.ClassType);
NotifyGlobalLoading;
finally
EndGlobalLoading;
end;
end;
function CreateLRSReader(s: TStream; var DestroyDriver: boolean): TReader;
var
p: Pointer;
Driver: TAbstractObjectReader;
begin
Result:=TReader.Create(s,4096);
//If included Default translator LRSTranslator will be set
if Assigned(LRSTranslator) then
Result.OnReadStringProperty:=@(LRSTranslator.TranslateStringProperty);
Result.OnPropertyNotFound := @(PropertiesToSkip.DoPropertyNotFound);
DestroyDriver:=false;
if Result.Driver.ClassType=LRSObjectReaderClass then
begin
TLRSObjectReader(Result.Driver).Reader:=Result;
exit;
end;
// hack to set a write protected variable.
// DestroyDriver:=true; TReader will free it
Driver:=LRSObjectReaderClass.Create(s,4096);
p:=@Result.Driver;
Result.Driver.Free;
TAbstractObjectReader(p^):=Driver;
TLRSObjectReader(Driver).Reader:=Result;
end;
function CreateLRSWriter(s: TStream; var DestroyDriver: boolean): TWriter;
var
Driver: TAbstractObjectWriter;
begin
Driver:=LRSObjectWriterClass.Create(s,4096);
DestroyDriver:=true;
Result:=TWriter.Create(Driver);
TLRSObjectWriter(Driver).Writer:=Result;
end;
{ LRS format converter functions }
procedure ReverseBytes(p: Pointer; Count: integer);
var
p1: PChar;
p2: PChar;
c: Char;
begin
p1:=PChar(p);
p2:=PChar(p)+Count-1;
while p1<p2 do begin
c:=p1^;
p1^:=p2^;
p2^:=c;
inc(p1);
dec(p2);
end;
end;
procedure ReverseByteOrderInWords(p: PWord; Count: integer);
var
i: Integer;
w: Word;
begin
for i:=0 to Count-1 do begin
w:=p[i];
w:=(w shr 8) or ((w and $ff) shl 8);
p[i]:=w;
end;
end;
function ConvertLRSExtendedToDouble(p: Pointer): Double;
type
Ti386ExtendedReversed = packed record
{$IFDEF FPC_BIG_ENDIAN}
ExponentAndSign: word;
Mantissa: qword;
{$ELSE}
Mantissa: qword;
ExponentAndSign: word;
{$ENDIF}
end;
var
e: Ti386ExtendedReversed;
Exponent: word;
ExponentAndSign: word;
Mantissa: qword;
begin
System.Move(p^,e,10);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@e,10);
{$ENDIF}
// i386 extended
Exponent:=(e.ExponentAndSign and $7fff);
if (Exponent>$4000+$3ff) or (Exponent<$4000-$400) then begin
// exponent out of bounds
Result:=0;
exit;
end;
dec(Exponent,$4000-$400);
ExponentAndSign:=Exponent or ((e.ExponentAndSign and $8000) shr 4);
// i386 extended has leading 1, double has not (shl 1)
// i386 has 64 bit, double has 52 bit (shr 12)
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
{$IFDEF FPC_BIG_ENDIAN}
// accessing Mantissa will couse trouble, copy it first
System.Move(e.Mantissa, Mantissa, SizeOf(Mantissa));
Mantissa := (Mantissa shl 1) shr 12;
{$ELSE FPC_BIG_ENDIAN}
Mantissa := (e.Mantissa shl 1) shr 12;
{$ENDIF FPC_BIG_ENDIAN}
{$ELSE FPC_REQUIRES_PROPER_ALIGNMENT}
Mantissa := (e.Mantissa shl 1) shr 12;
{$ENDIF FPC_REQUIRES_PROPER_ALIGNMENT}
// put together
QWord(Result):=Mantissa or (qword(ExponentAndSign) shl 52);
end;
procedure ConvertEndianBigDoubleToLRSExtended(BigEndianDouble, LRSExtended: Pointer);
// Floats consists of a sign bit, some exponent bits and the mantissa bits
// A 0 is all bits 0
// not 0 has always a leading 1, which exponent is stored
// Single/Double does not save the leading 1, Extended does.
//
// Double is 8 bytes long, leftmost bit is sign,
// then 11 bit exponent based $400, then 52 bit mantissa without leading 1
//
// Extended is 10 bytes long, leftmost bit is sign,
// then 15 bit exponent based $4000, then 64 bit mantissa with leading 1
// EndianLittle means reversed byte order
var
e: array[0..9] of byte;
i: Integer;
Exponent: Word;
d: PByte;
begin
d:=PByte(BigEndianDouble);
// convert ppc double to i386 extended
if (PCardinal(d)[0] or PCardinal(d)[1])=0 then begin
// 0
FillChar(LRSExtended^,10,#0);
end else begin
Exponent:=((d[0] and $7f) shl 4)+(d[1] shr 4);
inc(Exponent,$4000-$400);
if (d[0] and $80)>0 then
// signed
inc(Exponent,$8000);
e[9]:=Exponent shr 8;
e[8]:=Exponent and $ff;
e[7]:=($80 or (d[1] shl 3) or (d[2] shr 5)) and $ff;
for i:=3 to 7 do begin
e[9-i]:=((d[i-1] shl 3) or (d[i] shr 5)) and $ff;
end;
e[1]:=(d[7] shl 3) and $ff;
e[0]:=0;
System.Move(e[0],LRSExtended^,10);
end;
end;
procedure ConvertLEDoubleToLRSExtended(LEDouble, LRSExtended: Pointer);
type
TMantissaWrap = record
case boolean of
True: (Q: QWord);
False: (B: array[0..7] of Byte);
end;
TExpWrap = packed record
Mantissa: TMantissaWrap;
Exp: Word;
end;
var
Q: PQWord absolute LEDouble;
C: PCardinal absolute LEDouble;
W: PWord absolute LEDouble;
E: ^TExpWrap absolute LRSExtended;
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
Mantissa: TMantissaWrap;
{$endif}
begin
if W[3] and $7FF0 = $7FF0 // infinite or NaN
then E^.Exp := $7FFF
else E^.Exp := (W[3] and $7FFF) shr 4 - $3FF + $3FFF;
E^.Exp := E^.Exp or (W[3] and $8000); // sign
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
Mantissa.Q := (Q^ shl 11);
Mantissa.B[7] := Mantissa.B[7] or $80; // add ignored 1
System.Move(Mantissa, E^.Mantissa, 8);
{$else}
E^.Mantissa.Q := (Q^ shl 11);
E^.Mantissa.B[7] := E^.Mantissa.B[7] or $80; // add ignored 1
{$endif}
end;
function ReadLRSShortInt(s: TStream): shortint;
begin
Result:=0;
s.Read(Result,1);
end;
function ReadLRSByte(s: TStream): byte;
begin
Result:=0;
s.Read(Result,1);
end;
function ReadLRSWord(s: TStream): word;
begin
Result:=0;
s.Read(Result,2);
{$IFDEF FPC_BIG_ENDIAN}
Result:=((Result and $ff) shl 8) or (Result shr 8);
{$ENDIF}
end;
function ReadLRSSmallInt(s: TStream): smallint;
begin
Result:=0;
{$IFDEF FPC_BIG_ENDIAN}
Result:=smallint(ReadLRSWord(s));
{$ELSE}
s.Read(Result,2);
{$ENDIF}
end;
function ReadLRSInteger(s: TStream): integer;
begin
Result:=0;
s.Read(Result,4);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,4);
{$ENDIF}
end;
function ReadLRSCardinal(s: TStream): cardinal;
begin
Result:=0;
s.Read(Result,4);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,4);
{$ENDIF}
end;
function ReadLRSInt64(s: TStream): int64;
begin
Result:=0;
s.Read(Result,8);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,8);
{$ENDIF}
end;
function ReadLRSSingle(s: TStream): Single;
begin
Result:=0;
s.Read(Result,4);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,4);
{$ENDIF}
end;
function ReadLRSDouble(s: TStream): Double;
begin
Result:=0;
s.Read(Result,8);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,8);
{$ENDIF}
end;
function ReadLRSExtended(s: TStream): Extended;
begin
Result:=0;
{$IFDEF FPC_HAS_TYPE_EXTENDED}
s.Read(Result,10);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,10);
{$ENDIF}
{$ELSE}
// possible endian conversion is handled in ConvertLRSExtendedToDouble
Result:=ReadLRSEndianLittleExtendedAsDouble(s);
{$ENDIF}
end;
function ReadLRSCurrency(s: TStream): Currency;
begin
Result:=0;
s.Read(Result,8);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@Result,8);
{$ENDIF}
end;
function ReadLRSWideString(s: TStream): WideString;
var
Len: LongInt;
begin
Len:=ReadLRSInteger(s);
SetLength(Result,Len);
if Len>0 then begin
s.Read(Result[1],Len*2);
{$IFDEF FPC_BIG_ENDIAN}
ReverseByteOrderInWords(PWord(@Result[1]),Len);
{$ENDIF}
end;
end;
function ReadLRSEndianLittleExtendedAsDouble(s: TStream): Double;
var
e: array[1..10] of byte;
begin
s.Read(e,10);
Result:=ConvertLRSExtendedToDouble(@e);
end;
function ReadLRSValueType(s: TStream): TValueType;
var
b: byte;
begin
s.Read(b,1);
Result:=TValueType(b);
end;
function ReadLRSInt64MB(s: TStream): int64;
var
v: TValueType;
begin
v:=ReadLRSValueType(s);
case v of
vaInt8: Result:=ReadLRSShortInt(s);
vaInt16: Result:=ReadLRSSmallInt(s);
vaInt32: Result:=ReadLRSInteger(s);
vaInt64: Result:=ReadLRSInt64(s);
else
raise EInOutError.Create('ordinal valuetype missing');
end;
end;
procedure WriteLRSReversedWord(s: TStream; w: word);
begin
w:=(w shr 8) or ((w and $ff) shl 8);
s.Write(w,2);
end;
procedure WriteLRS4BytesReversed(s: TStream; p: Pointer);
var
a: array[0..3] of char;
i: Integer;
begin
for i:=0 to 3 do
a[i]:=PChar(p)[3-i];
s.Write(a[0],4);
end;
procedure WriteLRS8BytesReversed(s: TStream; p: Pointer);
var
a: array[0..7] of char;
i: Integer;
begin
for i:=0 to 7 do
a[i]:=PChar(p)[7-i];
s.Write(a[0],8);
end;
procedure WriteLRS10BytesReversed(s: TStream; p: Pointer);
var
a: array[0..9] of char;
i: Integer;
begin
for i:=0 to 9 do
a[i]:=PChar(p)[9-i];
s.Write(a[0],10);
end;
procedure WriteLRSReversedWords(s: TStream; p: Pointer; Count: integer);
var
w: Word;
i: Integer;
begin
for i:=0 to Count-1 do begin
w:=PWord(P)[i];
w:=(w shr 8) or ((w and $ff) shl 8);
s.Write(w,2);
end;
end;
function FloatToLFMStr(const Value: extended; Precision, Digits: Integer): string;
var
P: Integer;
TooSmall, TooLarge: Boolean;
DeletePos: LongInt;
begin
Result:='';
If (Precision = -1) or (Precision > 15) then Precision := 15;
TooSmall := (Abs(Value) < 0.00001) and (Value>0.0);
if TooSmall then begin
P := 0;
TooLarge := False;
end
else begin
Str(Value:digits:precision, Result);
P := Pos('.', Result);
TooLarge :=(P > Precision + 1) or (Pos('E', Result)<>0);
End;
if TooSmall or TooLarge then begin
// use exponential format
Str(Value:Precision + 8, Result);
P:=4;
while (P>0) and (Digits < P) and (Result[Precision + 5] = '0') do begin
if P<>1 then
system.Delete(Result, Precision + 5, 1)
else
system.Delete(Result, Precision + 3, 3);
Dec(P);
end;
if Result[1] = ' ' then
System.Delete(Result, 1, 1);
// Strip unneeded zeroes.
P:=Pos('E',result)-1;
If P>=0 then begin
{ delete superfluous +? }
if result[p+2]='+' then
system.Delete(Result,P+2,1);
DeletePos:=p;
while (DeletePos>1) and (Result[DeletePos]='0') do
Dec(DeletePos);
if (DeletePos>0) and (Result[DeletePos]=DefaultFormatSettings.DecimalSeparator) Then
Dec(DeletePos);
if (DeletePos<p) then
system.Delete(Result,DeletePos,p-DeletePos);
end;
end
else if (P<>0) then begin
// we have a decimalseparator
P := Length(Result);
While (P>0) and (Result[P] = '0') Do
Dec(P);
If (P>0) and (Result[P]=DefaultFormatSettings.DecimalSeparator) Then
Dec(P);
SetLength(Result, P);
end;
end;
function CompareLRPositionLinkWithLFMPosition(Item1, Item2: Pointer): integer;
var
p1: Int64;
p2: Int64;
begin
p1:=PLRPositionLink(Item1)^.LFMPosition;
p2:=PLRPositionLink(Item2)^.LFMPosition;
if p1<p2 then
Result:=1
else if p1>p2 then
Result:=-1
else
Result:=0;
end;
function CompareLRPositionLinkWithLRSPosition(Item1, Item2: Pointer): integer;
var
p1: Int64;
p2: Int64;
begin
p1:=PLRPositionLink(Item1)^.LRSPosition;
p2:=PLRPositionLink(Item2)^.LRSPosition;
if p1<p2 then
Result:=1
else if p1>p2 then
Result:=-1
else
Result:=0;
end;
procedure RegisterPropertyToSkip(PersistentClass: TPersistentClass;
const PropertyName, Note, HelpKeyWord: string);
begin
PropertiesToSkip.Add(PersistentClass, PropertyName, Note, HelpKeyWord);
end;
procedure Register;
begin
RegisterComponents('System',[TLazComponentQueue]);
end;
procedure WriteLRSNull(s: TStream; Count: integer);
var
c: char;
i: Integer;
begin
c:=#0;
for i:=0 to Count-1 do
s.Write(c,1);
end;
procedure WriteLRSEndianBigDoubleAsEndianLittleExtended(s: TStream;
EndBigDouble: PByte);
var
e: array[0..9] of byte;
begin
ConvertEndianBigDoubleToLRSExtended(EndBigDouble,@e);
s.Write(e[0],10);
end;
procedure WriteLRSDoubleAsExtended(s: TStream; ADouble: PByte);
var
e: array[0..9] of byte;
begin
{$ifdef FPC_LITTLE_ENDIAN}
ConvertLEDoubleToLRSExtended(ADouble,@e);
{$else}
ConvertEndianBigDoubleToLRSExtended(ADouble,@e);
{$endif}
s.Write(e[0],10);
end;
procedure WriteLRSSmallInt(s: TStream; const i: SmallInt);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(i,2);
{$ELSE}
WriteLRSReversedWord(s,Word(i));
{$ENDIF}
end;
procedure WriteLRSWord(s: TStream; const w: word);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(w,2);
{$ELSE}
WriteLRSReversedWord(s,w);
{$ENDIF}
end;
procedure WriteLRSInteger(s: TStream; const i: integer);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(i,4);
{$ELSE}
WriteLRS4BytesReversed(s,@i);
{$ENDIF}
end;
procedure WriteLRSCardinal(s: TStream; const c: cardinal);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(c,4);
{$ELSE}
WriteLRS4BytesReversed(s,@c);
{$ENDIF}
end;
procedure WriteLRSSingle(s: TStream; const si: Single);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(si,4);
{$ELSE}
WriteLRS4BytesReversed(s,@si);
{$ENDIF}
end;
procedure WriteLRSDouble(s: TStream; const d: Double);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(d,8);
{$ELSE}
WriteLRS8BytesReversed(s,@d);
{$ENDIF}
end;
procedure WriteLRSExtended(s: TStream; const e: extended);
begin
{$IFDEF FPC_HAS_TYPE_EXTENDED}
{$IFDEF FPC_BIG_ENDIAN}
WriteLRS10BytesReversed(s, @e);
{$ELSE}
s.Write(e,10);
{$ENDIF}
{$ELSE}
WriteLRSDoubleAsExtended(s,pbyte(@e))
{$ENDIF}
end;
procedure WriteLRSInt64(s: TStream; const i: int64);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(i,8);
{$ELSE}
WriteLRS8BytesReversed(s,@i);
{$ENDIF}
end;
procedure WriteLRSCurrency(s: TStream; const c: Currency);
begin
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(c,8);
{$ELSE}
WriteLRS8BytesReversed(s,@c);
{$ENDIF}
end;
procedure WriteLRSWideStringContent(s: TStream; const w: WideString);
var
Size: Integer;
begin
Size:=length(w);
if Size=0 then exit;
{$IFDEF FPC_LITTLE_ENDIAN}
s.Write(w[1], Size * 2);
{$ELSE}
WriteLRSReversedWords(s,@w[1],Size);
{$ENDIF}
end;
procedure WriteLRSInt64MB(s: TStream; const Value: int64);
var
w: Word;
i: Integer;
b: Byte;
begin
// Use the smallest possible integer type for the given value:
if (Value >= -128) and (Value <= 127) then
begin
b:=byte(vaInt8);
s.Write(b, 1);
b:=byte(Value);
s.Write(b, 1);
end else if (Value >= -32768) and (Value <= 32767) then
begin
b:=byte(vaInt16);
s.Write(b, 1);
w:=Word(Value);
WriteLRSWord(s,w);
end else if (Value >= -$80000000) and (Value <= $7fffffff) then
begin
b:=byte(vaInt32);
s.Write(b, 1);
i:=Integer(Value);
WriteLRSInteger(s,i);
end else
begin
b:=byte(vaInt64);
s.Write(b, 1);
WriteLRSInt64(s,Value);
end;
end;
{ TLRSObjectReader }
procedure TLRSObjectReader.Read(var Buf; Count: LongInt);
var
CopyNow: LongInt;
Dest: Pointer;
begin
Dest := @Buf;
while Count > 0 do
begin
if FBufPos >= FBufEnd then
begin
FBufEnd := FStream.Read(FBuffer^, FBufSize);
if FBufEnd = 0 then
raise EReadError.Create('Read Error');
FBufPos := 0;
end;
CopyNow := FBufEnd - FBufPos;
if CopyNow > Count then
CopyNow := Count;
Move(PChar(FBuffer)[FBufPos], Dest^, CopyNow);
Inc(FBufPos, CopyNow);
Dest:=Dest+CopyNow;
Dec(Count, CopyNow);
end;
end;
procedure TLRSObjectReader.SkipProperty;
begin
{ Skip property name, then the property value }
ReadStr;
SkipValue;
end;
procedure TLRSObjectReader.SkipSetBody;
begin
while Length(ReadStr) > 0 do;
end;
procedure TLRSObjectReader.Push(ItemType: TLRSItemType; const AName: string;
Root: TComponent; PushCount: integer);
begin
if FStackPointer=FStackCapacity then begin
FStackCapacity:=FStackCapacity*2+10;
ReAllocMem(FStack,SizeOf(TLRSORStackItem)*FStackCapacity);
FillByte(FStack[FStackPointer],SizeOf(TLRSORStackItem)*(FStackCapacity-FStackPointer),0);
end;
//DebugLn(['TLRSObjectReader.Push AName=',AName,' Type=', GetEnumName(TypeInfo(TLRSItemType), Integer(ItemType)),' PushCount=',PushCount]);
FStack[FStackPointer].Name:=AName;
FStack[FStackPointer].ItemType:=ItemType;
FStack[FStackPointer].Root:=Root;
FStack[FStackPointer].PushCount:=PushCount;
FStack[FStackPointer].ItemNr:=-1;
inc(FStackPointer);
end;
procedure TLRSObjectReader.Pop;
var
Item: PLRSORStackItem;
begin
if FStackPointer=0 then
raise Exception.Create('Error: TLRSObjectReader.Pop stack is empty');
Item:=@FStack[FStackPointer-1];
//DebugLn(['TLRSObjectReader.Pop AName=',Item^.Name,
// ' Type=',GetEnumName(TypeInfo(TLRSItemType), Integer(item^.ItemType)),
// ' PushCount=',item^.PushCount,' StackPtr=', FStackPointer]);
if Item^.PushCount>1 then begin
// stack item still needs more EndList
dec(Item^.PushCount);
end else begin
// stack item is complete
dec(FStackPointer);
end;
end;
procedure TLRSObjectReader.ClearStack;
var
i: Integer;
begin
for i:=0 to FStackCapacity-1 do begin
FStack[i].Name:='';
end;
ReAllocMem(FStack,0);
end;
function TLRSObjectReader.InternalReadValue: TValueType;
var
b: byte;
begin
Result := vaNull; { Necessary in FPC as TValueType is larger than 1 byte! }
Read(b,1);
Result:=TValueType(b);
end;
function TLRSObjectReader.ReadIntegerContent: integer;
begin
Result:=0;
Read(Result,4);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,4);
{$endif}
end;
constructor TLRSObjectReader.Create(AStream: TStream; BufSize: Integer);
begin
inherited Create;
FStream := AStream;
FBufSize := BufSize;
GetMem(FBuffer, BufSize);
end;
destructor TLRSObjectReader.Destroy;
begin
{ Seek back the amount of bytes that we didn't process until now: }
if Assigned(FStream) then
FStream.Seek(Integer(FBufPos) - Integer(FBufEnd), soFromCurrent);
if Assigned(FBuffer) then
FreeMem(FBuffer, FBufSize);
ClearStack;
inherited Destroy;
end;
function TLRSObjectReader.ReadValue: TValueType;
begin
Result := InternalReadValue;
case Result of
vaNull:
begin
EndPropertyIfOpen;
// End previous element collection, list or component.
if FStackPointer > 0 then
Pop;
end;
vaCollection:
begin
Push(lrsitCollection);
end;
vaList:
begin
// Increase counter for next collection item.
if (FStackPointer > 0) and (FStack[FStackPointer-1].ItemType = lrsitCollection) then
Inc(FStack[FStackPointer-1].ItemNr);
Push(lrsitList);
end;
end;
end;
function TLRSObjectReader.NextValue: TValueType;
begin
Result := InternalReadValue;
{ We only 'peek' at the next value, so seek back to unget the read value: }
Dec(FBufPos);
end;
procedure TLRSObjectReader.BeginRootComponent;
var
Signature: TFilerSignature;
begin
{ Read filer signature }
Signature:='1234';
Read(Signature[1],length(Signature));
if Signature <> FilerSignature then
raise EReadError.Create('Invalid Filer Signature');
end;
procedure TLRSObjectReader.BeginComponent(var Flags: TFilerFlags;
var AChildPos: Integer; var CompClassName, CompName: String);
var
Prefix: Byte;
ValueType: TValueType;
ItemName: String;
ItemRoot: TComponent;
begin
{ Every component can start with a special prefix: }
Flags := [];
if (Byte(NextValue) and $f0) = $f0 then
begin
Prefix := Byte(ReadValue);
if (ObjStreamMaskInherited and Prefix)<>0 then
Include(Flags,ffInherited);
if (ObjStreamMaskInline and Prefix)<>0 then
Include(Flags,ffInline);
if (ObjStreamMaskChildPos and Prefix)<>0 then
begin
Include(Flags,ffChildPos);
ValueType := ReadValue;
case ValueType of
vaInt8:
AChildPos := ReadInt8;
vaInt16:
AChildPos := ReadInt16;
vaInt32:
AChildPos := ReadInt32;
else
PropValueError;
end;
end;
end;
CompClassName := ReadStr;
CompName := ReadStr;
// Top component is addressed by ClassName.
if FStackPointer = 0 then
begin
ItemName := CompClassName;
ItemRoot := nil;
end
else
begin
ItemName := CompName;
if Assigned(Reader) then
// Reader.LookupRoot is the current Root component.
ItemRoot := Reader.LookupRoot
else
ItemRoot := nil;
end;
// A component has two lists: properties and childs, hence PopCount=2.
Push(lrsitComponent, ItemName, ItemRoot, 2);
end;
function TLRSObjectReader.BeginProperty: String;
begin
EndPropertyIfOpen;
Result := ReadStr;
Push(lrsitProperty, Result);
end;
procedure TLRSObjectReader.EndPropertyIfOpen;
begin
// End previous property.
if (FStackPointer > 0) and (FStack[FStackPointer-1].ItemType = lrsitProperty) then
Pop;
end;
function TLRSObjectReader.GetStackPath: string;
var
i: Integer;
CurName: string;
Item: PLRSORStackItem;
begin
Result:='';
for i:=0 to FStackPointer-1 do
begin
Item := @FStack[i];
// Reader.Root is the top component in the module.
if Assigned(Reader) and
(Item^.ItemType = lrsitComponent) and
(Item^.Root = Reader.Root) and
(Item^.Root <> nil) then
begin
// Restart path from top component.
Result := Item^.Root.ClassName;
end;
CurName:=Item^.Name;
if CurName<>'' then begin
if Result<>'' then Result:=Result+'.';
Result:=Result+CurName;
end;
if Item^.ItemNr >= 0 then
Result := Result + '[' + IntToStr(Item^.ItemNr) + ']';
end;
end;
procedure TLRSObjectReader.ReadBinary(const DestData: TMemoryStream);
var
BinSize: LongInt;
begin
BinSize:=ReadIntegerContent;
DestData.Size := BinSize;
Read(DestData.Memory^, BinSize);
end;
function TLRSObjectReader.ReadFloat: Extended;
{$ifndef FPC_HAS_TYPE_EXTENDED}
var
e: array[1..10] of byte;
{$endif}
begin
Result:=0;
{$ifdef FPC_HAS_TYPE_EXTENDED}
Read(Result, 10);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result, 10);
{$endif FPC_BIG_ENDIAN}
{$else FPC_HAS_TYPE_EXTENDED}
Read(e, 10);
Result := ConvertLRSExtendedToDouble(@e);
{$endif FPC_HAS_TYPE_EXTENDED}
end;
function TLRSObjectReader.ReadSingle: Single;
begin
Result:=0;
Read(Result, 4);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,4);
{$endif}
end;
function TLRSObjectReader.ReadCurrency: Currency;
begin
Result:=0;
Read(Result, 8);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,8);
{$endif}
end;
function TLRSObjectReader.ReadDate: TDateTime;
begin
Result:=0;
Read(Result, 8);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,8);
{$endif}
end;
function TLRSObjectReader.ReadIdent(ValueType: TValueType): String;
var
b: Byte;
begin
case ValueType of
vaIdent:
begin
Read(b, 1);
SetLength(Result, b);
if ( b > 0 ) then
Read(Result[1], b);
end;
vaNil:
Result := 'nil';
vaFalse:
Result := 'False';
vaTrue:
Result := 'True';
vaNull:
Result := 'Null';
else
Result:='';
RaiseGDBException('');
end;
end;
function TLRSObjectReader.ReadInt8: ShortInt;
begin
Result:=0;
Read(Result, 1);
end;
function TLRSObjectReader.ReadInt16: SmallInt;
begin
Result:=0;
Read(Result, 2);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,2);
{$endif}
end;
function TLRSObjectReader.ReadInt32: LongInt;
begin
Result:=0;
Read(Result, 4);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,4);
{$endif}
end;
function TLRSObjectReader.ReadInt64: Int64;
begin
Result:=0;
Read(Result, 8);
{$ifdef FPC_BIG_ENDIAN}
ReverseBytes(@Result,8);
{$endif}
end;
function TLRSObjectReader.ReadSet(EnumType: Pointer): Integer;
type
tset = set of 0..31;
var
OName: String;
OValue: Integer;
begin
try
Result := 0;
while True do
begin
OName := ReadStr;
if Length(OName) = 0 then
break;
OValue := GetEnumValue(PTypeInfo(EnumType), OName);
// Eg. "Options" is a set and can give an error when changing component type.
// Do nothing on error (OValue = -1), was PropValueError; (JuMa)
if OValue >= 0 then
include(tset(result),OValue);
end;
except
SkipSetBody;
raise;
end;
end;
procedure TLRSObjectReader.ReadSignature;
begin
end;
function TLRSObjectReader.ReadStr: String;
var
b: Byte;
begin
Read(b, 1);
SetLength(Result, b);
if b > 0 then
Read(Result[1], b);
end;
function TLRSObjectReader.ReadString(StringType: TValueType): String;
var
i: Integer;
b: byte;
begin
case StringType of
vaString:
begin
Read(b, 1);
i:=b;
end;
vaLString:
i:=ReadIntegerContent;
else
raise Exception.Create('TLRSObjectReader.ReadString invalid StringType');
end;
SetLength(Result, i);
if i > 0 then
Read(Pointer(@Result[1])^, i);
end;
function TLRSObjectReader.ReadWideString: WideString;
var
i: Integer;
begin
i:=ReadIntegerContent;
SetLength(Result, i);
if i > 0 then
Read(Pointer(@Result[1])^, i*2);
//debugln('TLRSObjectReader.ReadWideString ',Result);
end;
function TLRSObjectReader.ReadUnicodeString: UnicodeString;
var
i: Integer;
begin
i:=ReadIntegerContent;
SetLength(Result, i);
if i > 0 then
Read(Pointer(@Result[1])^, i*2);
//debugln('TLRSObjectReader.ReadWideString ',Result);
end;
procedure TLRSObjectReader.SkipComponent(SkipComponentInfos: Boolean);
var
Flags: TFilerFlags;
Dummy: Integer;
CompClassName, CompName: String;
begin
if SkipComponentInfos then
{ Skip prefix, component class name and component object name }
BeginComponent(Flags, Dummy, CompClassName, CompName);
{ Skip properties }
while NextValue <> vaNull do
SkipProperty;
ReadValue;
{ Skip children }
while NextValue <> vaNull do
SkipComponent(True);
ReadValue;
end;
procedure TLRSObjectReader.SkipValue;
procedure SkipBytes(Count: LongInt);
var
Dummy: array[0..1023] of Byte;
SkipNow: Integer;
begin
while Count > 0 do
begin
if Count > 1024 then
SkipNow := 1024
else
SkipNow := Count;
Read(Dummy, SkipNow);
Dec(Count, SkipNow);
end;
end;
var
Count: LongInt;
begin
case ReadValue of
vaNull, vaFalse, vaTrue, vaNil: ;
vaList:
begin
while NextValue <> vaNull do
SkipValue;
ReadValue;
end;
vaInt8:
SkipBytes(1);
vaInt16:
SkipBytes(2);
vaInt32:
SkipBytes(4);
vaExtended:
SkipBytes(10);
vaString, vaIdent:
ReadStr;
vaBinary, vaLString:
begin
Count:=ReadIntegerContent;
SkipBytes(Count);
end;
vaWString, vaUString:
begin
Count:=ReadIntegerContent;
SkipBytes(Count*2);
end;
vaSet:
SkipSetBody;
vaCollection:
begin
while NextValue <> vaNull do
begin
{ Skip the order value if present }
if NextValue in [vaInt8, vaInt16, vaInt32] then
SkipValue;
SkipBytes(1);
while NextValue <> vaNull do
SkipProperty;
ReadValue;
end;
ReadValue;
end;
vaSingle:
SkipBytes(4);
vaCurrency:
SkipBytes(SizeOf(Currency));
vaDate:
SkipBytes(8);
vaInt64:
SkipBytes(8);
else
RaiseGDBException('TLRSObjectReader.SkipValue unknown valuetype');
end;
end;
{ TLRSObjectWriter }
procedure TLRSObjectWriter.Push(ItemType: TLRSItemType; const AName: string;
Root: TComponent; PushCount: integer;
SkipIfEmpty: boolean);
begin
if FStackPointer=FStackCapacity then begin
FStackCapacity:=FStackCapacity*2+10;
ReAllocMem(FStack,SizeOf(TLRSOWStackItem)*FStackCapacity);
FillByte(FStack[FStackPointer],SizeOf(TLRSOWStackItem)*(FStackCapacity-FStackPointer),0);
end;
//if AName<>'' then DebugLn(['TLRSObjectWriter.Push AName=',AName, ' Type=', GetEnumName(TypeInfo(TLRSItemType), Integer(ItemType)),' PushCount=',PushCount,' SkipIfEmpty=',SkipIfEmpty]);
FStack[FStackPointer].Name:=AName;
FStack[FStackPointer].ItemType:=ItemType;
FStack[FStackPointer].Root:=Root;
FStack[FStackPointer].PushCount:=PushCount;
FStack[FStackPointer].ItemNr:=-1;
FStack[FStackPointer].SkipIfEmpty:=SkipIfEmpty;
FStack[FStackPointer].BufCount:=0;
if SkipIfEmpty then
FStack[FStackPointer].State:=lrsowsisStarted
else begin
FlushStackToStream;
FStack[FStackPointer].State:=lrsowsisDataWritten;
end;
inc(FStackPointer);
end;
procedure TLRSObjectWriter.EndHeader;
var
Item: PLRSOWStackItem;
begin
Item:=@FStack[FStackPointer-1];
if Item^.State=lrsowsisStarted then
Item^.State:=lrsowsisHeaderWritten;
end;
procedure TLRSObjectWriter.Pop(WriteNull: boolean);
var
Item: PLRSOWStackItem;
begin
if FStackPointer=0 then
raise Exception.Create('Error: TLRSObjectWriter.Pop stack is empty');
Item:=@FStack[FStackPointer-1];
if Item^.PushCount>1 then begin
// stack item still needs more EndList
dec(Item^.PushCount);
if WriteNull then begin
if Item^.State=lrsowsisHeaderWritten then begin
// no data yet, append EndList to header
Item^.State:=lrsowsisStarted;
WriteValue(vaNull);
// wait again for data
Item^.State:=lrsowsisHeaderWritten;
end else begin
// write EndList to stream
WriteValue(vaNull);
end;
end;
end else begin
// stack item is complete
dec(FStackPointer);
//if Item^.BufCount>0 then DebugLn(['TLRSObjectWriter.Pop SKIPPED: ',Item^.Name]);
if (Item^.State=lrsowsisDataWritten) and WriteNull then
WriteValue(vaNull);
end;
end;
procedure TLRSObjectWriter.ClearStack;
var
i: Integer;
begin
for i:=0 to FStackCapacity-1 do begin
FStack[i].Name:='';
ReAllocMem(FStack[i].Buffer,0);
end;
ReAllocMem(FStack,0);
end;
procedure TLRSObjectWriter.FlushStackToStream;
var
i: Integer;
Item: PLRSOWStackItem;
begin
for i:=0 to FStackPointer-1 do begin
Item:=@FStack[i];
if Item^.State<>lrsowsisDataWritten then begin
//DebugLn(['TLRSObjectWriter.Write FLUSH from stack to stream']);
Item^.State:=lrsowsisDataWritten;
WriteToStream(Item^.Buffer^,Item^.BufCount);
Item^.BufCount:=0;
end;
end;
end;
procedure TLRSObjectWriter.WriteToStream(const Buffer; Count: Longint);
var
CopyNow: LongInt;
SourceBuf: PChar;
begin
//DebugLn(['TLRSObjectWriter.WriteToStream ',dbgMemRange(@Buffer,Count,80)]);
if Count<2*FBufSize then begin
// write a small amount of data
SourceBuf:=@Buffer;
while Count > 0 do
begin
CopyNow := Count;
if CopyNow > FBufSize - FBufPos then
CopyNow := FBufSize - FBufPos;
Move(SourceBuf^, PChar(FBuffer)[FBufPos], CopyNow);
Dec(Count, CopyNow);
Inc(FBufPos, CopyNow);
SourceBuf:=SourceBuf+CopyNow;
if FBufPos = FBufSize then
FlushBuffer;
end;
end else begin
// write a big amount of data
if FBufPos>0 then
FlushBuffer;
FStream.WriteBuffer(Buffer, Count);
end;
end;
procedure TLRSObjectWriter.FlushBuffer;
begin
FStream.WriteBuffer(FBuffer^, FBufPos);
FBufPos := 0;
end;
procedure TLRSObjectWriter.Write(const Buffer; Count: Longint);
var
Item: PLRSOWStackItem;
begin
if Count=0 then exit;
if (FStackPointer>0) then
begin
Item:=@FStack[FStackPointer-1];
case Item^.State of
lrsowsisStarted:
begin
// store data on stack
//DebugLn(['TLRSObjectWriter.Write STORE data on stack']);
if Item^.BufCount+Count>Item^.BufCapacity then
begin
Item^.BufCapacity:=Item^.BufCount+Count+10;
ReAllocMem(Item^.Buffer,Item^.BufCapacity);
end;
System.Move(Buffer,PByte(Item^.Buffer)[Item^.BufCount],Count);
inc(Item^.BufCount,Count);
exit;
end;
lrsowsisHeaderWritten:
begin
// flush header(s) from stack to stream
FlushStackToStream;
end;
end;
end;
// write data to stream
WriteToStream(Buffer,Count);
end;
procedure TLRSObjectWriter.WriteValue(Value: TValueType);
var
b: byte;
begin
b:=byte(Value);
Write(b, 1);
end;
procedure TLRSObjectWriter.WriteStr(const Value: String);
var
i: Integer;
b: Byte;
begin
i := Length(Value);
if i > 255 then
i := 255;
b:=byte(i);
Write(b,1);
if i > 0 then
Write(Value[1], i);
end;
procedure TLRSObjectWriter.WriteIntegerContent(i: integer);
begin
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@i,4);
{$ENDIF}
Write(i,4);
end;
procedure TLRSObjectWriter.WriteWordContent(w: word);
begin
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@w,2);
{$ENDIF}
Write(w,2);
end;
procedure TLRSObjectWriter.WriteInt64Content(i: int64);
begin
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@i,8);
{$ENDIF}
Write(i,8);
end;
procedure TLRSObjectWriter.WriteSingleContent(s: single);
begin
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@s,4);
{$ENDIF}
Write(s,4);
end;
procedure TLRSObjectWriter.WriteDoubleContent(d: Double);
begin
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@d,8);
{$ENDIF}
Write(d,8);
end;
procedure TLRSObjectWriter.WriteExtendedContent(e: Extended);
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
var
LRSExtended: array[1..10] of byte;
{$endif}
begin
{$IFDEF FPC_HAS_TYPE_EXTENDED}
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@e,10);
{$ENDIF}
Write(e,10);
{$ELSE}
{$IFDEF FPC_BIG_ENDIAN}
ConvertEndianBigDoubleToLRSExtended(@e,@LRSExtended);
{$ELSE}
ConvertLEDoubleToLRSExtended(@e,@LRSExtended);
{$ENDIF}
Write(LRSExtended,10);
{$ENDIF}
end;
procedure TLRSObjectWriter.WriteCurrencyContent(c: Currency);
begin
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@c,8);
{$ENDIF}
Write(c,8);
end;
procedure TLRSObjectWriter.WriteWideStringContent(const ws: WideString);
begin
if ws='' then exit;
{$IFDEF FPC_BIG_ENDIAN}
WriteWordsReversed(PWord(@ws[1]),length(ws));
{$ELSE}
Write(ws[1],length(ws)*2);
{$ENDIF}
end;
procedure TLRSObjectWriter.WriteWordsReversed(p: PWord; Count: integer);
var
i: Integer;
w: Word;
begin
for i:=0 to Count-1 do begin
w:=p[i];
w:=((w and $ff) shl 8) or (w and $ff);
Write(w,2);
end;
end;
procedure TLRSObjectWriter.WriteNulls(Count: integer);
var
c: Char;
i: Integer;
begin
c:=#0;
for i:=0 to Count-1 do Write(c,1);
end;
constructor TLRSObjectWriter.Create(Stream: TStream; BufSize: Integer);
begin
inherited Create;
FStream := Stream;
FBufSize := BufSize;
GetMem(FBuffer, BufSize);
end;
destructor TLRSObjectWriter.Destroy;
begin
// Flush all data which hasn't been written yet
if Assigned(FStream) then
FlushBuffer;
if Assigned(FBuffer) then begin
FreeMem(FBuffer, FBufSize);
FBuffer:=nil;
end;
ClearStack;
inherited Destroy;
end;
procedure TLRSObjectWriter.BeginCollection;
begin
//DebugLn(['TLRSObjectWriter.BeginCollection ',FStackPointer]);
Push(lrsitCollection);
WriteValue(vaCollection);
end;
procedure TLRSObjectWriter.BeginComponent(Component: TComponent;
Flags: TFilerFlags; ChildPos: Integer);
var
Prefix: Byte;
CanBeOmitted: boolean;
ItemName: String;
ItemRoot: TComponent;
begin
//DebugLn(['TLRSObjectWriter.BeginComponent ',FStackPointer]);
// an inherited child component can be omitted if empty
CanBeOmitted:=(not WriteEmptyInheritedChilds)
and (FStackPointer>0) and (ffInherited in Flags)
and (not (ffChildPos in Flags));
// Top component is addressed by ClassName.
if FStackPointer = 0 then
begin
ItemName := Component.ClassName;
ItemRoot := nil;
end
else
begin
ItemName := Component.Name;
if Assigned(Writer) then
// Writer.Root is the current Root component.
ItemRoot := Writer.Root
else
ItemRoot := nil;
end;
// A component has two lists: properties and childs, hence PopCount=2.
Push(lrsitComponent, ItemName, ItemRoot, 2, CanBeOmitted);
if not FSignatureWritten then
begin
Write(FilerSignature[1], length(FilerSignature));
FSignatureWritten := True;
end;
{ Only write the flags if they are needed! }
if Flags <> [] then
begin
Prefix := $f0;
if ffInherited in Flags then
inc(Prefix,ObjStreamMaskInherited);
if ffInline in Flags then
inc(Prefix,ObjStreamMaskInline);
if ffChildPos in Flags then
inc(Prefix,ObjStreamMaskChildPos);
Write(Prefix, 1);
if ffChildPos in Flags then
WriteInteger(ChildPos);
end;
WriteStr(Component.ClassName);
WriteStr(Component.Name);
EndHeader;
end;
procedure TLRSObjectWriter.WriteSignature;
begin
end;
procedure TLRSObjectWriter.BeginList;
begin
// Increase counter for next collection item.
if (FStackPointer > 0) and (FStack[FStackPointer-1].ItemType = lrsitCollection) then
Inc(FStack[FStackPointer-1].ItemNr);
//DebugLn(['TLRSObjectWriter.BeginList ',FStackPointer]);
Push(lrsitList);
WriteValue(vaList);
end;
procedure TLRSObjectWriter.EndList;
begin
//DebugLn(['TLRSObjectWriter.EndList ',FStackPointer]);
Pop(true);
end;
procedure TLRSObjectWriter.BeginProperty(const PropName: String);
begin
//DebugLn(['TLRSObjectWriter.BeginProperty ',FStackPointer,' ',PropName]);
Push(lrsitProperty, PropName);
WriteStr(PropName);
end;
procedure TLRSObjectWriter.EndProperty;
begin
//DebugLn(['TLRSObjectWriter.EndProperty ',FStackPointer]);
Pop(false);
end;
function TLRSObjectWriter.GetStackPath: string;
var
i: Integer;
CurName: string;
Item: PLRSOWStackItem;
begin
Result:='';
for i:=0 to FStackPointer-1 do
begin
Item := @FStack[i];
// Writer.LookupRoot is the top component in the module.
if Assigned(Writer) and
(Item^.ItemType = lrsitComponent) and
(Item^.Root = Writer.LookupRoot) and
(Item^.Root <> nil) then
begin
// Restart path from top component.
Result := Item^.Root.ClassName;
end;
CurName:=Item^.Name;
if CurName<>'' then begin
if Result<>'' then Result:=Result+'.';
Result:=Result+CurName;
end;
if Item^.ItemNr >= 0 then
Result := Result + '[' + IntToStr(Item^.ItemNr) + ']';
end;
end;
procedure TLRSObjectWriter.WriteBinary(const Buffer; Count: LongInt);
begin
WriteValue(vaBinary);
WriteIntegerContent(Count);
Write(Buffer, Count);
end;
procedure TLRSObjectWriter.WriteBoolean(Value: Boolean);
begin
if Value then
WriteValue(vaTrue)
else
WriteValue(vaFalse);
end;
procedure TLRSObjectWriter.WriteFloat(const Value: Extended);
begin
WriteValue(vaExtended);
WriteExtendedContent(Value);
end;
procedure TLRSObjectWriter.WriteSingle(const Value: Single);
begin
WriteValue(vaSingle);
WriteSingleContent(Value);
end;
procedure TLRSObjectWriter.WriteCurrency(const Value: Currency);
begin
WriteValue(vaCurrency);
WriteCurrencyContent(Value);
end;
procedure TLRSObjectWriter.WriteDate(const Value: TDateTime);
begin
WriteValue(vaDate);
WriteDoubleContent(Value);
end;
procedure TLRSObjectWriter.WriteIdent(const Ident: string);
begin
{ Check if Ident is a special identifier before trying to just write
Ident directly }
if UpperCase(Ident) = 'NIL' then
WriteValue(vaNil)
else if UpperCase(Ident) = 'FALSE' then
WriteValue(vaFalse)
else if UpperCase(Ident) = 'TRUE' then
WriteValue(vaTrue)
else if UpperCase(Ident) = 'NULL' then
WriteValue(vaNull) else
begin
WriteValue(vaIdent);
WriteStr(Ident);
end;
end;
procedure TLRSObjectWriter.WriteInteger(Value: Int64);
var
w: Word;
i: Integer;
b: Byte;
begin
//debugln('TLRSObjectWriter.WriteInteger Value=',Value);
// Use the smallest possible integer type for the given value:
if (Value >= -128) and (Value <= 127) then
begin
WriteValue(vaInt8);
b:=Byte(Value);
Write(b, 1);
end else if (Value >= -32768) and (Value <= 32767) then
begin
WriteValue(vaInt16);
w:=Word(Value);
WriteWordContent(w);
end else if (Value >= -$80000000) and (Value <= $7fffffff) then
begin
WriteValue(vaInt32);
i:=Integer(Value);
WriteIntegerContent(i);
end else
begin
WriteValue(vaInt64);
WriteInt64Content(Value);
end;
end;
procedure TLRSObjectWriter.WriteMethodName(const Name: String);
begin
if Length(Name) > 0 then
begin
WriteValue(vaIdent);
WriteStr(Name);
end else
WriteValue(vaNil);
end;
procedure TLRSObjectWriter.WriteSet(Value: LongInt; SetType: Pointer);
type
tset = set of 0..31;
var
i: Integer;
begin
WriteValue(vaSet);
for i := 0 to 31 do
begin
if (i in tset(Value)) then
WriteStr(GetEnumName(PTypeInfo(SetType), i));
end;
WriteStr('');
end;
procedure TLRSObjectWriter.WriteString(const Value: TLazObjectWriterString);
var
i: Integer;
b: Byte;
begin
i := Length(Value);
if i <= 255 then
begin
WriteValue(vaString);
b:=byte(i);
Write(b, 1);
end else
begin
WriteValue(vaLString);
WriteIntegerContent(i);
end;
if i > 0 then
Write(Value[1], i);
end;
procedure TLRSObjectWriter.WriteWideString(const Value: WideString);
var
i: Integer;
begin
WriteValue(vaWString);
i := Length(Value);
WriteIntegerContent(i);
WriteWideStringContent(Value);
end;
procedure TLRSObjectWriter.WriteUnicodeString(const Value: UnicodeString);
var
i: Integer;
begin
WriteValue(vaUString);
i := Length(Value);
WriteIntegerContent(i);
WriteWideStringContent(Value);
end;
procedure TLRSObjectWriter.WriteVariant(const Value: Variant);
begin
case VarType(Value) of
varnull:
WriteValue(vaNull);
varsmallint, varinteger, varshortint, varint64, varbyte, varword, varlongword, varqword:
WriteInteger(Value);
varsingle:
WriteSingle(Value);
vardouble:
WriteFloat(Value);
vardate:
WriteDate(Value);
varcurrency:
WriteCurrency(Value);
varolestr, varstring:
WriteString(String(Value));
varboolean:
WriteBoolean(Value);
else
WriteValue(vaNil);
end;
end;
{ TLRPositionLinks }
function TLRPositionLinks.GetLFM(Index: integer): Int64;
begin
Result:=PLRPositionLink(FItems[Index])^.LFMPosition;
end;
function TLRPositionLinks.GetData(Index: integer): Pointer;
begin
Result:=PLRPositionLink(FItems[Index])^.Data;
end;
function TLRPositionLinks.GetLRS(Index: integer): Int64;
begin
Result:=PLRPositionLink(FItems[Index])^.LRSPosition;
end;
procedure TLRPositionLinks.SetCount(const AValue: integer);
var
i: LongInt;
Item: PLRPositionLink;
begin
if FCount=AValue then exit;
// free old items
for i:=AValue to FCount-1 do begin
Item:=PLRPositionLink(FItems[i]);
Dispose(Item);
end;
// create new items
FItems.Count:=AValue;
for i:=FCount to AValue-1 do begin
New(Item);
Item^.LFMPosition:=-1;
Item^.LRSPosition:=-1;
Item^.Data:=nil;
FItems[i]:=Item;
end;
FCount:=AValue;
end;
procedure TLRPositionLinks.SetData(Index: integer; const AValue: Pointer);
begin
PLRPositionLink(FItems[Index])^.Data:=AValue;
end;
procedure TLRPositionLinks.SetLFM(Index: integer; const AValue: Int64);
begin
PLRPositionLink(FItems[Index])^.LFMPosition:=AValue;
end;
procedure TLRPositionLinks.SetLRS(Index: integer; const AValue: Int64);
begin
PLRPositionLink(FItems[Index])^.LRSPosition:=AValue;
end;
constructor TLRPositionLinks.Create;
begin
FItems:=TFPList.Create;
end;
destructor TLRPositionLinks.Destroy;
begin
Count:=0;
FItems.Free;
inherited Destroy;
end;
procedure TLRPositionLinks.Clear;
begin
Count:=0;
end;
procedure TLRPositionLinks.Sort(LFMPositions: Boolean);
begin
if LFMPositions then
FItems.Sort(@CompareLRPositionLinkWithLFMPosition)
else
FItems.Sort(@CompareLRPositionLinkWithLRSPosition)
end;
function TLRPositionLinks.IndexOf(const Position: int64; LFMPositions: Boolean
): integer;
var
l, r, m: integer;
p: Int64;
begin
// binary search for the line
l:=0;
r:=FCount-1;
while r>=l do begin
m:=(l+r) shr 1;
if LFMPositions then
p:=PLRPositionLink(FItems[m])^.LFMPosition
else
p:=PLRPositionLink(FItems[m])^.LRSPosition;
if p>Position then begin
// too high, search lower
r:=m-1;
end else if p<Position then begin
// too low, search higher
l:=m+1;
end else begin
// position found
Result:=m;
exit;
end;
end;
Result:=-1;
end;
function TLRPositionLinks.IndexOfRange(const FromPos, ToPos: int64;
LFMPositions: Boolean): integer;
var
l, r, m: integer;
p: Int64;
Item: PLRPositionLink;
begin
// binary search for the line
l:=0;
r:=FCount-1;
while r>=l do begin
m:=(l+r) shr 1;
Item:=PLRPositionLink(FItems[m]);
if LFMPositions then
p:=Item^.LFMPosition
else
p:=Item^.LRSPosition;
if p>=ToPos then begin
// too high, search lower
r:=m-1;
end else if p<FromPos then begin
// too low, search higher
l:=m+1;
end else begin
// position found
Result:=m;
exit;
end;
end;
Result:=-1;
end;
procedure TLRPositionLinks.SetPosition(const FromPos, ToPos, MappedPos: int64;
LFMtoLRSPositions: Boolean);
var
i: LongInt;
begin
i:=IndexOfRange(FromPos,ToPos,LFMtoLRSPositions);
if i>=0 then
if LFMtoLRSPositions then
PLRPositionLink(FItems[i])^.LRSPosition:=MappedPos
else
PLRPositionLink(FItems[i])^.LFMPosition:=MappedPos;
end;
procedure TLRPositionLinks.Add(const LFMPos, LRSPos: Int64; AData: Pointer);
var
Item: PLRPositionLink;
begin
Count:=Count+1;
Item:=PLRPositionLink(FItems[Count-1]);
Item^.LFMPosition:=LFMPos;
Item^.LRSPosition:=LRSPos;
Item^.Data:=AData;
end;
{ TCustomLazComponentQueue }
function TCustomLazComponentQueue.ReadComponentSize(out ComponentSize,
SizeLength: int64): Boolean;
// returns true if there are enough bytes to read the ComponentSize
// and returns the ComponentSize
// and returns the size (SizeLength) needed to store the ComponentSize
procedure ReadBytes(var p);
var a: array[1..9] of byte;
begin
FQueue.Top(a[1],1+SizeLength);
System.Move(a[2],p,SizeLength);
{$IFDEF FPC_BIG_ENDIAN}
ReverseBytes(@p,SizeLength);
{$ENDIF}
end;
var
v8: ShortInt;
v16: SmallInt;
v32: Integer;
v64: int64;
vt: TValueType;
begin
Result:=false;
// check if there are enough bytes
if (FQueue.Size<2) then exit;
FQueue.Top(vt,1);
case vt of
vaInt8: SizeLength:=1;
vaInt16: SizeLength:=2;
vaInt32: SizeLength:=4;
vaInt64: SizeLength:=8;
else
raise EInOutError.Create('Invalid size type');
end;
if FQueue.Size<1+SizeLength then exit; // need more data
// read the ComponentSize
Result:=true;
case vt of
vaInt8:
begin
ReadBytes(v8);
ComponentSize:=v8;
end;
vaInt16:
begin
ReadBytes(v16);
ComponentSize:=v16;
end;
vaInt32:
begin
ReadBytes(v32);
ComponentSize:=v32;
end;
vaInt64:
begin
ReadBytes(v64);
ComponentSize:=v64;
end;
end;
inc(SizeLength);
if ComponentSize<0 then
raise EInOutError.Create('Size of data in queue is negative');
end;
constructor TCustomLazComponentQueue.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FQueue:=TDynamicDataQueue.Create;
end;
destructor TCustomLazComponentQueue.Destroy;
begin
FreeAndNil(FQueue);
inherited Destroy;
end;
procedure TCustomLazComponentQueue.Clear;
begin
FQueue.Clear;
end;
function TCustomLazComponentQueue.Write(const Buffer; Count: Longint): Longint;
begin
Result:=FQueue.Push(Buffer,Count);
end;
function TCustomLazComponentQueue.CopyFrom(AStream: TStream; Count: Longint
): Longint;
begin
Result:=FQueue.Push(AStream,Count);
end;
function TCustomLazComponentQueue.HasComponent: Boolean;
var
ComponentSize, SizeLength: int64;
begin
if not ReadComponentSize(ComponentSize,SizeLength) then exit(false);
Result:=FQueue.Size-SizeLength>=ComponentSize;
end;
function TCustomLazComponentQueue.ReadComponent(var AComponent: TComponent;
NewOwner: TComponent): Boolean;
var
ComponentSize, SizeLength: int64;
AStream: TMemoryStream;
begin
Result:=false;
if not ReadComponentSize(ComponentSize,SizeLength) then exit;
if (FQueue.Size-SizeLength<ComponentSize) then exit;
// a complete component is in the buffer -> copy it to a stream
AStream:=TMemoryStream.Create;
try
// copy component to stream
AStream.Size:=SizeLength+ComponentSize;
FQueue.Pop(AStream,SizeLength+ComponentSize);
// create/read the component
AStream.Position:=SizeLength;
ReadComponentFromBinaryStream(AStream,AComponent,
OnFindComponentClass,NewOwner);
finally
AStream.Free;
end;
Result:=true;
end;
function TCustomLazComponentQueue.ConvertComponentAsString(AComponent: TComponent
): string;
var
AStream: TMemoryStream;
ComponentSize: Int64;
LengthSize: Int64;
begin
// write component to stream
AStream:=TMemoryStream.Create;
try
WriteComponentAsBinaryToStream(AStream,AComponent);
ComponentSize:=AStream.Size;
WriteLRSInt64MB(AStream,ComponentSize);
LengthSize:=AStream.Size-ComponentSize;
//debugln('TCustomLazComponentQueue.ConvertComponentAsString ComponentSize=',ComponentSize,' LengthSize=',LengthSize);
SetLength(Result,AStream.Size);
// write size
AStream.Position:=ComponentSize;
AStream.Read(Result[1],LengthSize);
//debugln('TCustomLazComponentQueue.ConvertComponentAsString ',hexstr(ord(Result[1]),2),' ',hexstr(ord(Result[2]),2),' ',hexstr(ord(Result[3]),2),' ',hexstr(ord(Result[4]),2));
// write component
AStream.Position:=0;
AStream.Read(Result[LengthSize+1],ComponentSize);
finally
AStream.Free;
end;
end;
{ TLazarusResourceStream }
procedure TLazarusResourceStream.Initialize(Name, ResType: PChar);
begin
if ResType <> nil then
FLRes := LazarusResources.Find(Name, ResType)
else
FLRes := LazarusResources.Find(Name);
if FLRes = nil then
raise EResNotFound.CreateFmt(SResNotFound, [Name]);
SetPointer(PChar(FLRes.Value), Length(FLRes.Value));
end;
constructor TLazarusResourceStream.Create(const ResName: string; ResType: PChar);
begin
inherited Create;
Initialize(PChar(ResName), ResType);
end;
constructor TLazarusResourceStream.CreateFromID(ResID: Integer; ResType: PChar);
begin
inherited Create;
Initialize(PChar(PtrInt(ResID)), ResType);
end;
constructor TLazarusResourceStream.CreateFromHandle(AHandle: TLResource);
begin
inherited Create;
FLRes := AHandle;
SetPointer(PChar(FLRes.Value), Length(FLRes.Value));
end;
{$ifdef UseRes}
constructor TLazarusResourceStream.CreateFromHandle(Instance: TFPResourceHMODULE; AHandle: TFPResourceHandle);
begin
FPRes := LoadResource(Instance, AHandle);
if FPRes <> 0 then
SetPointer(LockResource(FPRes), SizeOfResource(Instance, AHandle));
end;
{$endif}
destructor TLazarusResourceStream.Destroy;
begin
{$ifdef UseRES}
if FPRes <> 0 then
begin
UnlockResource(FPRes);
FreeResource(FPRes);
end;
{$endif}
inherited Destroy;
end;
function TLazarusResourceStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := 0;
raise EStreamError.Create(SCantWriteResourceStreamError);
end;
const
ParseBufSize = 4096;
LastSpecialToken = 5;
TokNames : array[0..LastSpecialToken] of string =
(
'EOF',
'Symbol',
'String',
'Integer',
'Float',
'WideString'
);
function TUTF8Parser.GetTokenName(aTok: char): string;
begin
if ord(aTok) <= LastSpecialToken then
Result:=TokNames[ord(aTok)]
else Result:=aTok;
end;
procedure TUTF8Parser.LoadBuffer;
var newread : integer;
begin
newread:=fStream.Read(fBuf[0],ParseBufSize);
fBuf[newread]:=#0;
fLineStart:=fLineStart-fPos; // column = fPos - fLineStart + 1
fPos:=0;
fBufLen:=newread;
fEofReached:=newread=0;
end;
procedure TUTF8Parser.CheckLoadBuffer; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
if fBuf[fPos]<>#0 then exit;
if fPos<fBufLen then begin
// skip #0
repeat
inc(fPos);
if fBuf[fPos]<>#0 then exit;
until (fPos=fBufLen);
end;
LoadBuffer;
end;
procedure TUTF8Parser.ProcessChar; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
fLastTokenStr:=fLastTokenStr+fBuf[fPos];
inc(fPos);
CheckLoadBuffer;
end;
function TUTF8Parser.IsNumber: boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
Result:=fBuf[fPos] in ['0'..'9'];
end;
function TUTF8Parser.IsHexNum: boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
Result:=fBuf[fPos] in ['0'..'9','A'..'F','a'..'f'];
end;
function TUTF8Parser.IsAlpha: boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
Result:=fBuf[fPos] in ['_','A'..'Z','a'..'z'];
end;
function TUTF8Parser.IsAlphaNum: boolean; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
Result:=IsAlpha or IsNumber;
end;
function TUTF8Parser.GetHexValue(c: char): byte; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
begin
case c of
'0'..'9' : Result:=ord(c)-$30;
'A'..'F' : Result:=ord(c)-$37; //-$41+$0A
'a'..'f' : Result:=ord(c)-$57; //-$61+$0A
end;
end;
function TUTF8Parser.GetAlphaNum: string;
begin
if not IsAlpha then
ErrorFmt(SParExpected,[GetTokenName(toSymbol)]);
Result:='';
while IsAlphaNum do
begin
Result:=Result+fBuf[fPos];
inc(fPos);
CheckLoadBuffer;
end;
end;
procedure TUTF8Parser.HandleNewLine;
begin
if fBuf[fPos]=#13 then //CR
begin
inc(fPos);
CheckLoadBuffer;
if fBuf[fPos]=#10 then inc(fPos); //CR LF
end
else
inc(fPos); //LF
CheckLoadBuffer;
inc(fSourceLine);
fLineStart:=fPos;
end;
procedure TUTF8Parser.SkipSpaces;
begin
while fBuf[fPos] in [' ',#9] do begin
inc(fPos);
CheckLoadBuffer;
end;
end;
procedure TUTF8Parser.SkipWhitespace;
begin
while true do
begin
case fBuf[fPos] of
' ',#9 : SkipSpaces;
#10,#13 : HandleNewLine
else break;
end;
end;
end;
procedure TUTF8Parser.HandleEof;
begin
fToken:=toEOF;
fLastTokenStr:='';
end;
procedure TUTF8Parser.HandleAlphaNum;
begin
fLastTokenStr:=GetAlphaNum;
fToken:=toSymbol;
end;
procedure TUTF8Parser.HandleNumber;
type
floatPunct = (fpDot,fpE);
floatPuncts = set of floatPunct;
var
allowed : floatPuncts;
begin
fLastTokenStr:='';
while IsNumber do
ProcessChar;
fToken:=toInteger;
if (fBuf[fPos] in ['.','e','E']) then
begin
fToken:=toFloat;
allowed:=[fpDot,fpE];
while (fBuf[fPos] in ['.','e','E','0'..'9']) do
begin
case fBuf[fPos] of
'.' : if fpDot in allowed then Exclude(allowed,fpDot) else break;
'E','e' : if fpE in allowed then
begin
allowed:=[];
ProcessChar;
if (fBuf[fPos] in ['+','-']) then ProcessChar;
if not (fBuf[fPos] in ['0'..'9']) then
ErrorFmt(SParInvalidFloat,[fLastTokenStr+fBuf[fPos]]);
end
else break;
end;
ProcessChar;
end;
end;
if (fBuf[fPos] in ['s','S','d','D','c','C']) then //single, date, currency
begin
fFloatType:=fBuf[fPos];
inc(fPos);
CheckLoadBuffer;
fToken:=toFloat;
end
else fFloatType:=#0;
end;
procedure TUTF8Parser.HandleHexNumber;
var valid : boolean;
begin
fLastTokenStr:='$';
inc(fPos);
CheckLoadBuffer;
valid:=false;
while IsHexNum do
begin
valid:=true;
ProcessChar;
end;
if not valid then
ErrorFmt(SParInvalidInteger,[fLastTokenStr]);
fToken:=toInteger;
end;
function TUTF8Parser.HandleQuotedString: string;
begin
Result:='';
inc(fPos);
CheckLoadBuffer;
while true do
begin
case fBuf[fPos] of
#0 : ErrorStr(SParUnterminatedString);
#13,#10 : ErrorStr(SParUnterminatedString);
'''' : begin
inc(fPos);
CheckLoadBuffer;
if fBuf[fPos]<>'''' then exit;
end;
end;
Result:=Result+fBuf[fPos];
inc(fPos);
CheckLoadBuffer;
end;
end;
function TUTF8Parser.HandleDecimalString: string;
var
i: integer;
begin
Result:='';
inc(fPos);
CheckLoadBuffer;
while IsNumber do
begin
Result:=Result+fBuf[fPos];
inc(fPos);
CheckLoadBuffer;
end;
if not TryStrToInt(Result,i) then
i:=0;
Result:=UnicodeToUTF8(i); // widestring
end;
procedure TUTF8Parser.HandleString;
var
IsWideString: Boolean;
begin
fLastTokenStr:='';
IsWideString := false;
while true do begin
case fBuf[fPos] of
'''' : fLastTokenStr:=fLastTokenStr+HandleQuotedString;
'#' : begin
fLastTokenStr:=fLastTokenStr+HandleDecimalString;
IsWideString:=true;
end;
else break;
end;
end;
if IsWideString then
fToken:=Classes.toWString
else
fToken:=Classes.toString;
end;
procedure TUTF8Parser.HandleMinus;
begin
inc(fPos);
CheckLoadBuffer;
if IsNumber then
begin
HandleNumber;
fLastTokenStr:='-'+fLastTokenStr;
end
else
begin
fToken:='-';
fLastTokenStr:=fToken;
end;
end;
procedure TUTF8Parser.HandleUnknown;
begin
fToken:=fBuf[fPos];
fLastTokenStr:=fToken;
inc(fPos);
CheckLoadBuffer;
end;
constructor TUTF8Parser.Create(Stream: TStream);
begin
fStream:=Stream;
fBuf:=GetMem(ParseBufSize+1);
fBufLen:=0;
fPos:=0;
fLineStart:=0;
fSourceLine:=1;
fEofReached:=false;
fLastTokenStr:='';
fFloatType:=#0;
fToken:=#0;
LoadBuffer;
NextToken;
end;
destructor TUTF8Parser.Destroy;
begin
fStream.Position:=SourcePos;
FreeMem(fBuf);
end;
procedure TUTF8Parser.CheckToken(T: Char);
begin
if fToken<>T then
ErrorFmt(SParWrongTokenType,[GetTokenName(T),GetTokenName(fToken)]);
end;
procedure TUTF8Parser.CheckTokenSymbol(const S: string);
begin
CheckToken(toSymbol);
if CompareText(fLastTokenStr,S)<>0 then
ErrorFmt(SParWrongTokenSymbol,[s,fLastTokenStr]);
end;
procedure TUTF8Parser.Error(const Ident: string);
begin
ErrorStr(Ident);
end;
procedure TUTF8Parser.ErrorFmt(const Ident: string; const Args: array of const);
begin
ErrorStr(Format(Ident,Args));
end;
procedure TUTF8Parser.ErrorStr(const Message: string);
begin
debugln(['TUTF8Parser.ErrorStr Message="',Message,'" at y=',SourceLine,',x=',SourceColumn]);
raise EParserError.CreateFmt(Message+SParLocInfo,[SourceLine,SourceColumn,SourcePos]);
end;
procedure TUTF8Parser.HexToBinary(Stream: TStream);
var outbuf : array[0..ParseBufSize-1] of byte;
b : byte;
i : integer;
begin
i:=0;
SkipWhitespace;
while IsHexNum do
begin
b:=(GetHexValue(fBuf[fPos]) shl 4);
inc(fPos);
CheckLoadBuffer;
if not IsHexNum then
Error(SParUnterminatedBinValue);
b:=b or GetHexValue(fBuf[fPos]);
inc(fPos);
CheckLoadBuffer;
outbuf[i]:=b;
inc(i);
if i>=ParseBufSize then
begin
Stream.WriteBuffer(outbuf[0],i);
i:=0;
end;
SkipWhitespace;
end;
if i>0 then
Stream.WriteBuffer(outbuf[0],i);
NextToken;
end;
function TUTF8Parser.NextToken: Char;
begin
SkipWhiteSpace;
if fEofReached then
HandleEof
else
case fBuf[fPos] of
'_','A'..'Z','a'..'z' : HandleAlphaNum;
'$' : HandleHexNumber;
'-' : HandleMinus;
'0'..'9' : HandleNumber;
'''','#' : HandleString
else
HandleUnknown;
end;
Result:=fToken;
end;
function TUTF8Parser.SourcePos: Longint;
begin
Result:=fStream.Position-fBufLen+fPos;
end;
function TUTF8Parser.TokenComponentIdent: string;
begin
if fToken<>toSymbol then
ErrorFmt(SParExpected,[GetTokenName(toSymbol)]);
CheckLoadBuffer;
while fBuf[fPos]='.' do
begin
ProcessChar;
fLastTokenStr:=fLastTokenStr+GetAlphaNum;
end;
Result:=fLastTokenStr;
end;
function TUTF8Parser.TokenFloat: Extended;
var errcode : word;
begin
Val(fLastTokenStr,Result,errcode);
if errcode<>0 then
ErrorFmt(SParInvalidFloat,[fLastTokenStr]);
end;
function TUTF8Parser.TokenInt: Int64;
begin
if not TryStrToInt64(fLastTokenStr,Result) then
Result:=Int64(StrToQWord(fLastTokenStr)); //second chance for malformed files
end;
function TUTF8Parser.TokenString: string;
begin
case fToken of
toFloat : if fFloatType<>#0 then
Result:=fLastTokenStr+fFloatType
else Result:=fLastTokenStr
else
Result:=fLastTokenStr;
end;
end;
function TUTF8Parser.TokenSymbolIs(const S: string): Boolean;
begin
Result:=(fToken=toSymbol) and (CompareText(fLastTokenStr,S)=0);
end;
function TUTF8Parser.SourceColumn: integer;
begin
Result:=fPos-fLineStart+1;
end;
//------------------------------------------------------------------------------
procedure InternalInit;
begin
LazarusResources := TLResourceList.Create;
RegisterInitComponentHandler(TComponent, @InitResourceComponent);
PropertiesToSkip := TPropertiesToSkip.Create;
end;
initialization
InternalInit;
finalization
FreeAndNil(LazarusResources);
FreeAndNil(PropertiesToSkip);
end.