{ 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])=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$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 (DeletePos0) 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 p1p2 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 p1p2 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=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=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 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#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.