mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-04 11:23:45 +02:00

critical section, so that it can be entered recursively just like the one from TSimpleRWSync + test - reverted r14593, since the reason for using TRWSync instead of TMultiReadExclusiveWriteSynchronizer was because the former supported recursive write locks git-svn-id: trunk@14594 -
1741 lines
41 KiB
PHP
1741 lines
41 KiB
PHP
{
|
|
This file is part of the Free Component Library (FCL)
|
|
Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
|
|
{**********************************************************************
|
|
* Class implementations are in separate files. *
|
|
**********************************************************************}
|
|
|
|
var
|
|
ClassList : TThreadlist;
|
|
ClassAliasList : TStringList;
|
|
|
|
{
|
|
Include all message strings
|
|
|
|
Add a language with IFDEF LANG_NAME
|
|
just befor the final ELSE. This way English will always be the default.
|
|
}
|
|
|
|
{$IFDEF LANG_GERMAN}
|
|
{$i constsg.inc}
|
|
{$ELSE}
|
|
{$IFDEF LANG_SPANISH}
|
|
{$i constss.inc}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
{ Utility routines }
|
|
{$i util.inc}
|
|
|
|
{ TBits implementation }
|
|
{$i bits.inc}
|
|
|
|
{ All streams implementations: }
|
|
{ Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
|
|
{ TCustomMemoryStream TMemoryStream }
|
|
{$i streams.inc}
|
|
|
|
{ TParser implementation}
|
|
{$i parser.inc}
|
|
|
|
{ TCollection and TCollectionItem implementations }
|
|
{$i collect.inc}
|
|
|
|
{ TList and TThreadList implementations }
|
|
{$i lists.inc}
|
|
|
|
{ TStrings and TStringList implementations }
|
|
{$i stringl.inc}
|
|
|
|
{ TThread implementation }
|
|
|
|
{ system independend threading code }
|
|
var
|
|
{ event that happens when gui thread is done executing the method}
|
|
ExecuteEvent: PRtlEvent;
|
|
{ event executed by synchronize to wake main thread if it sleeps in CheckSynchronize }
|
|
SynchronizeTimeoutEvent: PRtlEvent;
|
|
{ guard for synchronization variables }
|
|
SynchronizeCritSect: TRtlCriticalSection;
|
|
{ method to execute }
|
|
SynchronizeMethod: TThreadMethod;
|
|
{ should we execute the method? }
|
|
DoSynchronizeMethod: boolean;
|
|
{ caught exception in gui thread, to be raised in calling thread }
|
|
SynchronizeException: Exception;
|
|
|
|
|
|
function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
|
|
var
|
|
FreeThread: Boolean;
|
|
Thread: TThread absolute ThreadObjPtr;
|
|
begin
|
|
{ if Suspend checks FSuspended before doing anything, make sure it }
|
|
{ knows we're currently not suspended (this flag may have been set }
|
|
{ to true if CreateSuspended was true) }
|
|
// Thread.FSuspended:=false;
|
|
// wait until AfterConstruction has been called, so we cannot
|
|
// free ourselves before TThread.Create has finished
|
|
// (since that one may check our VTM in case of $R+, and
|
|
// will call the AfterConstruction method in all cases)
|
|
// Thread.Suspend;
|
|
try
|
|
Thread.Execute;
|
|
except
|
|
Thread.FFatalException := TObject(AcquireExceptionObject);
|
|
end;
|
|
FreeThread := Thread.FFreeOnTerminate;
|
|
Result := Thread.FReturnValue;
|
|
Thread.FFinished := True;
|
|
Thread.DoTerminate;
|
|
if FreeThread then
|
|
Thread.Free;
|
|
EndThread(Result);
|
|
end;
|
|
|
|
{ system-dependent code }
|
|
{$i tthread.inc}
|
|
|
|
|
|
function TThread.GetSuspended: Boolean;
|
|
begin
|
|
GetSuspended:=FSuspended;
|
|
end;
|
|
|
|
|
|
procedure TThread.AfterConstruction;
|
|
begin
|
|
inherited AfterConstruction;
|
|
// Resume;
|
|
end;
|
|
|
|
|
|
class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
|
|
var
|
|
LocalSyncException: Exception;
|
|
begin
|
|
{ do we really need a synchronized call? }
|
|
if GetCurrentThreadID=MainThreadID then
|
|
AMethod()
|
|
else
|
|
begin
|
|
System.EnterCriticalSection(SynchronizeCritSect);
|
|
SynchronizeException:=nil;
|
|
SynchronizeMethod:=AMethod;
|
|
|
|
{ be careful, after this assignment Method could be already executed }
|
|
DoSynchronizeMethod:=true;
|
|
|
|
RtlEventSetEvent(SynchronizeTimeoutEvent);
|
|
|
|
if assigned(WakeMainThread) then
|
|
WakeMainThread(AThread);
|
|
|
|
{ wait infinitely }
|
|
RtlEventWaitFor(ExecuteEvent);
|
|
LocalSyncException:=SynchronizeException;
|
|
System.LeaveCriticalSection(SynchronizeCritSect);
|
|
if assigned(LocalSyncException) then
|
|
raise LocalSyncException;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TThread.Synchronize(AMethod: TThreadMethod);
|
|
begin
|
|
TThread.Synchronize(self,AMethod);
|
|
end;
|
|
|
|
|
|
function CheckSynchronize(timeout : longint=0) : boolean;
|
|
{ assumes being called from GUI thread }
|
|
begin
|
|
result:=false;
|
|
{ first sanity check }
|
|
if Not IsMultiThread then
|
|
Exit
|
|
{ second sanity check }
|
|
else if GetCurrentThreadID<>MainThreadID then
|
|
raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID])
|
|
else
|
|
begin
|
|
if timeout>0 then
|
|
begin
|
|
RtlEventWaitFor(SynchronizeTimeoutEvent,timeout);
|
|
end
|
|
else
|
|
RtlEventResetEvent(SynchronizeTimeoutEvent);
|
|
|
|
if DoSynchronizeMethod then
|
|
begin
|
|
DoSynchronizeMethod:=false;
|
|
try
|
|
SynchronizeMethod;
|
|
result:=true;
|
|
except
|
|
SynchronizeException:=Exception(AcquireExceptionObject);
|
|
end;
|
|
RtlEventSetEvent(ExecuteEvent);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TPersistent implementation }
|
|
{$i persist.inc }
|
|
|
|
{$i sllist.inc}
|
|
{$i resref.inc}
|
|
|
|
{ TComponent implementation }
|
|
{$i compon.inc}
|
|
|
|
{ TBasicAction implementation }
|
|
{$i action.inc}
|
|
|
|
{ TDataModule implementation }
|
|
{$i dm.inc}
|
|
|
|
{ Class and component registration routines }
|
|
{$I cregist.inc}
|
|
|
|
|
|
|
|
{ Interface related stuff }
|
|
{$I intf.inc}
|
|
|
|
{**********************************************************************
|
|
* Miscellaneous procedures and functions *
|
|
**********************************************************************}
|
|
|
|
function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PChar; Strings: TStrings): Integer;
|
|
var
|
|
b, c : pchar;
|
|
|
|
procedure SkipWhitespace;
|
|
begin
|
|
while (c^ in Whitespace) do
|
|
inc (c);
|
|
end;
|
|
|
|
procedure AddString;
|
|
var
|
|
l : integer;
|
|
s : string;
|
|
begin
|
|
l := c-b;
|
|
if l > 0 then
|
|
begin
|
|
if assigned(Strings) then
|
|
begin
|
|
setlength(s, l);
|
|
move (b^, s[1],l);
|
|
Strings.Add (s);
|
|
end;
|
|
inc (result);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
quoted : char;
|
|
begin
|
|
result := 0;
|
|
c := Content;
|
|
Quoted := #0;
|
|
Separators := Separators + [#13, #10] - ['''','"'];
|
|
SkipWhitespace;
|
|
b := c;
|
|
while (c^ <> #0) do
|
|
begin
|
|
if (c^ = Quoted) then
|
|
begin
|
|
if ((c+1)^ = Quoted) then
|
|
inc (c)
|
|
else
|
|
Quoted := #0
|
|
end
|
|
else if (Quoted = #0) and (c^ in ['''','"']) then
|
|
Quoted := c^;
|
|
if (Quoted = #0) and (c^ in Separators) then
|
|
begin
|
|
AddString;
|
|
inc (c);
|
|
SkipWhitespace;
|
|
b := c;
|
|
end
|
|
else
|
|
inc (c);
|
|
end;
|
|
if (c <> b) then
|
|
AddString;
|
|
end;
|
|
|
|
|
|
|
|
{ Point and rectangle constructors }
|
|
|
|
function Point(AX, AY: Integer): TPoint;
|
|
|
|
begin
|
|
with Result do
|
|
begin
|
|
X := AX;
|
|
Y := AY;
|
|
end;
|
|
end;
|
|
|
|
|
|
function SmallPoint(AX, AY: SmallInt): TSmallPoint;
|
|
|
|
begin
|
|
with Result do
|
|
begin
|
|
X := AX;
|
|
Y := AY;
|
|
end;
|
|
end;
|
|
|
|
|
|
function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
|
|
|
|
begin
|
|
with Result do
|
|
begin
|
|
Left := ALeft;
|
|
Top := ATop;
|
|
Right := ARight;
|
|
Bottom := ABottom;
|
|
end;
|
|
end;
|
|
|
|
|
|
function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
|
|
|
|
begin
|
|
with Result do
|
|
begin
|
|
Left := ALeft;
|
|
Top := ATop;
|
|
Right := ALeft + AWidth;
|
|
Bottom := ATop + AHeight;
|
|
end;
|
|
end;
|
|
|
|
|
|
function PointsEqual(const P1, P2: TPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
{ lazy, but should work }
|
|
result:=QWord(P1)=QWord(P2);
|
|
end;
|
|
|
|
|
|
function PointsEqual(const P1, P2: TSmallPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
{ lazy, but should work }
|
|
result:=DWord(P1)=DWord(P2);
|
|
end;
|
|
|
|
function InvalidPoint(X, Y: Integer): Boolean;
|
|
begin
|
|
result:=(X=-1) and (Y=-1);
|
|
end;
|
|
|
|
|
|
function InvalidPoint(const At: TPoint): Boolean;
|
|
begin
|
|
result:=(At.x=-1) and (At.y=-1);
|
|
end;
|
|
|
|
|
|
function InvalidPoint(const At: TSmallPoint): Boolean;
|
|
begin
|
|
result:=(At.x=-1) and (At.y=-1);
|
|
end;
|
|
|
|
|
|
{ Object filing routines }
|
|
|
|
var
|
|
IntConstList: TThreadList;
|
|
|
|
type
|
|
TIntConst = class
|
|
IntegerType: PTypeInfo; // The integer type RTTI pointer
|
|
IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
|
|
IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
|
|
constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
|
|
AIntToIdent: TIntToIdent);
|
|
end;
|
|
|
|
constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
|
|
AIntToIdent: TIntToIdent);
|
|
begin
|
|
IntegerType := AIntegerType;
|
|
IdentToIntFn := AIdentToInt;
|
|
IntToIdentFn := AIntToIdent;
|
|
end;
|
|
|
|
procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
|
|
IntToIdentFn: TIntToIdent);
|
|
begin
|
|
IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
|
|
end;
|
|
|
|
function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
with IntConstList.LockList do
|
|
try
|
|
for i := 0 to Count - 1 do
|
|
if TIntConst(Items[i]).IntegerType = AIntegerType then
|
|
exit(TIntConst(Items[i]).IntToIdentFn);
|
|
Result := nil;
|
|
finally
|
|
IntConstList.UnlockList;
|
|
end;
|
|
end;
|
|
|
|
function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
with IntConstList.LockList do
|
|
try
|
|
for i := 0 to Count - 1 do
|
|
with TIntConst(Items[I]) do
|
|
if TIntConst(Items[I]).IntegerType = AIntegerType then
|
|
exit(IdentToIntFn);
|
|
Result := nil;
|
|
finally
|
|
IntConstList.UnlockList;
|
|
end;
|
|
end;
|
|
|
|
function IdentToInt(const Ident: String; var Int: LongInt;
|
|
const Map: array of TIdentMapEntry): Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := Low(Map) to High(Map) do
|
|
if CompareText(Map[i].Name, Ident) = 0 then
|
|
begin
|
|
Int := Map[i].Value;
|
|
exit(True);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function IntToIdent(Int: LongInt; var Ident: String;
|
|
const Map: array of TIdentMapEntry): Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := Low(Map) to High(Map) do
|
|
if Map[i].Value = Int then
|
|
begin
|
|
Ident := Map[i].Name;
|
|
exit(True);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function GlobalIdentToInt(const Ident: String; var Int: LongInt):boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
with IntConstList.LockList do
|
|
try
|
|
for i := 0 to Count - 1 do
|
|
if TIntConst(Items[I]).IdentToIntFn(Ident, Int) then
|
|
Exit(True);
|
|
Result := false;
|
|
finally
|
|
IntConstList.UnlockList;
|
|
end;
|
|
end;
|
|
|
|
{ TPropFixup }
|
|
// Tainted. TPropFixup is being removed.
|
|
|
|
Type
|
|
TInitHandler = Class(TObject)
|
|
AHandler : TInitComponentHandler;
|
|
AClass : TComponentClass;
|
|
end;
|
|
|
|
Var
|
|
InitHandlerList : TList;
|
|
FindGlobalComponentList : TList;
|
|
|
|
procedure RegisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
|
|
begin
|
|
if not(assigned(FindGlobalComponentList)) then
|
|
FindGlobalComponentList:=TList.Create;
|
|
if FindGlobalComponentList.IndexOf(Pointer(AFindGlobalComponent))<0 then
|
|
FindGlobalComponentList.Add(Pointer(AFindGlobalComponent));
|
|
end;
|
|
|
|
|
|
procedure UnregisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
|
|
begin
|
|
if assigned(FindGlobalComponentList) then
|
|
FindGlobalComponentList.Remove(Pointer(AFindGlobalComponent));
|
|
end;
|
|
|
|
|
|
function FindGlobalComponent(const Name: string): TComponent;
|
|
var
|
|
i : sizeint;
|
|
begin
|
|
FindGlobalComponent:=nil;
|
|
if assigned(FindGlobalComponentList) then
|
|
begin
|
|
for i:=FindGlobalComponentList.Count-1 downto 0 do
|
|
begin
|
|
FindGlobalComponent:=TFindGlobalComponent(FindGlobalComponentList[i])(name);
|
|
if assigned(FindGlobalComponent) then
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler);
|
|
Var
|
|
I : Integer;
|
|
H: TInitHandler;
|
|
begin
|
|
If (InitHandlerList=Nil) then
|
|
InitHandlerList:=TList.Create;
|
|
H:=TInitHandler.Create;
|
|
H.Aclass:=ComponentClass;
|
|
H.AHandler:=Handler;
|
|
try
|
|
With InitHandlerList do
|
|
begin
|
|
I:=0;
|
|
While (I<Count) and not H.AClass.InheritsFrom(TInitHandler(Items[I]).AClass) do
|
|
Inc(I);
|
|
{ override? }
|
|
if (I<Count) and (TInitHandler(Items[I]).AClass=H.AClass) then
|
|
begin
|
|
TInitHandler(Items[I]).AHandler:=Handler;
|
|
H.Free;
|
|
end
|
|
else
|
|
InitHandlerList.Insert(I,H);
|
|
end;
|
|
except
|
|
H.Free;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ all targets should at least include the sysres.inc dummy in the system unit to compile this }
|
|
function CreateComponentfromRes(const res : string;Inst : THandle;var Component : TComponent) : Boolean;
|
|
var
|
|
ResStream : TResourceStream;
|
|
begin
|
|
result:=true;
|
|
|
|
if Inst=0 then
|
|
Inst:=HInstance;
|
|
|
|
try
|
|
ResStream:=TResourceStream.Create(Inst,res,RT_RCDATA);
|
|
try
|
|
Component:=ResStream.ReadComponent(Component);
|
|
finally
|
|
ResStream.Free;
|
|
end;
|
|
except
|
|
on EResNotFound do
|
|
result:=false;
|
|
end;
|
|
end;
|
|
|
|
|
|
function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean;
|
|
|
|
function doinit(_class : TClass) : boolean;
|
|
begin
|
|
result:=false;
|
|
if (_class.ClassType=TComponent) or (_class.ClassType=RootAncestor) then
|
|
exit;
|
|
result:=doinit(_class.ClassParent);
|
|
result:=CreateComponentfromRes(_class.ClassName,0,Instance) or result;
|
|
end;
|
|
|
|
begin
|
|
GlobalNameSpace.BeginWrite;
|
|
try
|
|
result:=doinit(Instance.ClassType);
|
|
finally
|
|
GlobalNameSpace.EndWrite;
|
|
end;
|
|
end;
|
|
|
|
|
|
function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
|
|
Var
|
|
I : Integer;
|
|
begin
|
|
I:=0;
|
|
if not Assigned(InitHandlerList) then begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
Result:=False;
|
|
With InitHandlerList do
|
|
begin
|
|
I:=0;
|
|
// Instance is the normally the lowest one, so that one should be used when searching.
|
|
While Not result and (I<Count) do
|
|
begin
|
|
If (Instance.InheritsFrom(TInitHandler(Items[i]).AClass)) then
|
|
Result:=TInitHandler(Items[i]).AHandler(Instance,RootAncestor);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
|
|
|
|
begin
|
|
{ !!!: Too Win32-specific }
|
|
InitComponentRes := False;
|
|
end;
|
|
|
|
|
|
function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
|
|
|
|
begin
|
|
{ !!!: Too Win32-specific }
|
|
ReadComponentRes := nil;
|
|
end;
|
|
|
|
|
|
function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
|
|
|
|
begin
|
|
{ !!!: Too Win32-specific in VCL }
|
|
ReadComponentResEx := nil;
|
|
end;
|
|
|
|
|
|
function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
|
|
var
|
|
FileStream: TStream;
|
|
begin
|
|
FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
|
|
try
|
|
Result := FileStream.ReadComponentRes(Instance);
|
|
finally
|
|
FileStream.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
|
|
var
|
|
FileStream: TStream;
|
|
begin
|
|
FileStream := TFileStream.Create(FileName, fmCreate);
|
|
try
|
|
FileStream.WriteComponentRes(Instance.ClassName, Instance);
|
|
finally
|
|
FileStream.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function FindNestedComponent(Root : TComponent; APath : String; CStyle : Boolean = True) : TComponent;
|
|
|
|
Function GetNextName : String; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
|
|
|
|
Var
|
|
P : Integer;
|
|
CM : Boolean;
|
|
|
|
begin
|
|
P:=Pos('.',APath);
|
|
CM:=False;
|
|
If (P=0) then
|
|
begin
|
|
If CStyle then
|
|
begin
|
|
P:=Pos('->',APath);
|
|
CM:=P<>0;
|
|
end;
|
|
If (P=0) Then
|
|
P:=Length(APath)+1;
|
|
end;
|
|
Result:=Copy(APath,1,P-1);
|
|
Delete(APath,1,P+Ord(CM));
|
|
end;
|
|
|
|
Var
|
|
C : TComponent;
|
|
S : String;
|
|
begin
|
|
If (APath='') then
|
|
Result:=Nil
|
|
else
|
|
begin
|
|
Result:=Root;
|
|
While (APath<>'') And (Result<>Nil) do
|
|
begin
|
|
C:=Result;
|
|
S:=Uppercase(GetNextName);
|
|
Result:=C.FindComponent(S);
|
|
If (Result=Nil) And (S='OWNER') then
|
|
Result:=C;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
threadvar
|
|
GlobalLoaded, GlobalLists: TList;
|
|
|
|
procedure BeginGlobalLoading;
|
|
|
|
begin
|
|
if not Assigned(GlobalLists) then
|
|
GlobalLists := TList.Create;
|
|
GlobalLists.Add(GlobalLoaded);
|
|
GlobalLoaded := TList.Create;
|
|
end;
|
|
|
|
|
|
{ Notify all global components that they have been loaded completely }
|
|
procedure NotifyGlobalLoading;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to GlobalLoaded.Count - 1 do
|
|
TComponent(GlobalLoaded[i]).Loaded;
|
|
end;
|
|
|
|
|
|
procedure EndGlobalLoading;
|
|
begin
|
|
{ Free the memory occupied by BeginGlobalLoading }
|
|
GlobalLoaded.Free;
|
|
GlobalLoaded := TList(GlobalLists.Last);
|
|
GlobalLists.Delete(GlobalLists.Count - 1);
|
|
if GlobalLists.Count = 0 then
|
|
begin
|
|
GlobalLists.Free;
|
|
GlobalLists := nil;
|
|
end;
|
|
end;
|
|
|
|
|
|
function CollectionsEqual(C1, C2: TCollection): Boolean;
|
|
begin
|
|
// !!!: Implement this
|
|
CollectionsEqual:=false;
|
|
end;
|
|
|
|
function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;
|
|
|
|
procedure stream_collection(s : tstream;c : tcollection;o : tcomponent);
|
|
var
|
|
w : twriter;
|
|
begin
|
|
w:=twriter.create(s,4096);
|
|
try
|
|
w.root:=o;
|
|
w.flookuproot:=o;
|
|
w.writecollection(c);
|
|
finally
|
|
w.free;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
s1,s2 : tmemorystream;
|
|
begin
|
|
result:=false;
|
|
if (c1.classtype<>c2.classtype) or
|
|
(c1.count<>c2.count) then
|
|
exit;
|
|
if c1.count = 0 then
|
|
begin
|
|
result:= true;
|
|
exit;
|
|
end;
|
|
s1:=tmemorystream.create;
|
|
try
|
|
s2:=tmemorystream.create;
|
|
try
|
|
stream_collection(s1,c1,owner1);
|
|
stream_collection(s2,c2,owner2);
|
|
result:=(s1.size=s2.size) and (CompareChar(s1.memory^,s2.memory^,s1.size)=0);
|
|
finally
|
|
s2.free;
|
|
end;
|
|
finally
|
|
s1.free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ Object conversion routines }
|
|
|
|
type
|
|
CharToOrdFuncty = Function(var charpo: Pointer): Cardinal;
|
|
|
|
function CharToOrd(var P: Pointer): Cardinal;
|
|
begin
|
|
result:= ord(pchar(P)^);
|
|
inc(pchar(P));
|
|
end;
|
|
|
|
function WideCharToOrd(var P: Pointer): Cardinal;
|
|
begin
|
|
result:= ord(pwidechar(P)^);
|
|
inc(pwidechar(P));
|
|
end;
|
|
|
|
function Utf8ToOrd(var P:Pointer): Cardinal;
|
|
begin
|
|
// Should also check for illegal utf8 combinations
|
|
Result := Ord(PChar(P)^);
|
|
Inc(P);
|
|
if (Result and $80) <> 0 then
|
|
if (Ord(Result) and %11100000) = %11000000 then begin
|
|
Result := ((Result and %00011111) shl 6)
|
|
or (ord(PChar(P)^) and %00111111);
|
|
Inc(P);
|
|
end else if (Ord(Result) and %11110000) = %11100000 then begin
|
|
Result := ((Result and %00011111) shl 12)
|
|
or ((ord(PChar(P)^) and %00111111) shl 6)
|
|
or (ord((PChar(P)+1)^) and %00111111);
|
|
Inc(P,2);
|
|
end else begin
|
|
Result := ((ord(Result) and %00011111) shl 18)
|
|
or ((ord(PChar(P)^) and %00111111) shl 12)
|
|
or ((ord((PChar(P)+1)^) and %00111111) shl 6)
|
|
or (ord((PChar(P)+2)^) and %00111111);
|
|
Inc(P,3);
|
|
end;
|
|
end;
|
|
|
|
procedure ObjectBinaryToText(Input, Output: TStream);
|
|
|
|
procedure OutStr(s: String);
|
|
begin
|
|
if Length(s) > 0 then
|
|
Output.Write(s[1], Length(s));
|
|
end;
|
|
|
|
procedure OutLn(s: String);
|
|
begin
|
|
OutStr(s + LineEnding);
|
|
end;
|
|
|
|
procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty);
|
|
|
|
var
|
|
res, NewStr: String;
|
|
w: Cardinal;
|
|
InString, NewInString: Boolean;
|
|
begin
|
|
if p = nil then begin
|
|
res:= '''''';
|
|
end
|
|
else
|
|
begin
|
|
res := '';
|
|
InString := False;
|
|
while P < LastP do
|
|
begin
|
|
NewInString := InString;
|
|
w := CharToOrdfunc(P);
|
|
if w = ord('''') then
|
|
begin //quote char
|
|
if not InString then
|
|
NewInString := True;
|
|
NewStr := '''''';
|
|
end
|
|
else if (Ord(w) >= 32) and (Ord(w) < 127) then
|
|
begin //printable ascii
|
|
if not InString then
|
|
NewInString := True;
|
|
NewStr := char(w);
|
|
end
|
|
else
|
|
begin //ascii control chars, non ascii
|
|
if InString then
|
|
NewInString := False;
|
|
NewStr := '#' + IntToStr(w);
|
|
end;
|
|
if NewInString <> InString then
|
|
begin
|
|
NewStr := '''' + NewStr;
|
|
InString := NewInString;
|
|
end;
|
|
res := res + NewStr;
|
|
end;
|
|
if InString then
|
|
res := res + '''';
|
|
end;
|
|
OutStr(res);
|
|
end;
|
|
|
|
procedure OutString(s: String);
|
|
begin
|
|
OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd);
|
|
end;
|
|
|
|
procedure OutWString(W: WideString);
|
|
begin
|
|
OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
|
|
end;
|
|
|
|
procedure OutUString(W: UnicodeString);
|
|
begin
|
|
OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
|
|
end;
|
|
|
|
procedure OutUtf8Str(s: String);
|
|
begin
|
|
OutChars(Pointer(S),PChar(S)+Length(S),@Utf8ToOrd);
|
|
end;
|
|
|
|
function ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
Result:=Input.ReadWord;
|
|
Result:=LEtoN(Result);
|
|
end;
|
|
|
|
function ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
Result:=Input.ReadDWord;
|
|
Result:=LEtoN(Result);
|
|
end;
|
|
|
|
function ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
Input.ReadBuffer(Result,sizeof(Result));
|
|
Result:=LEtoN(Result);
|
|
end;
|
|
|
|
{$ifndef FPUNONE}
|
|
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
|
|
function ExtendedToDouble(e : pointer) : double;
|
|
var mant : qword;
|
|
exp : smallint;
|
|
sign : boolean;
|
|
d : qword;
|
|
begin
|
|
move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
|
|
move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
|
|
mant:=LEtoN(mant);
|
|
exp:=LetoN(word(exp));
|
|
sign:=(exp and $8000)<>0;
|
|
if sign then exp:=exp and $7FFF;
|
|
case exp of
|
|
0 : mant:=0; //if denormalized, value is too small for double,
|
|
//so it's always zero
|
|
$7FFF : exp:=2047 //either infinity or NaN
|
|
else
|
|
begin
|
|
dec(exp,16383-1023);
|
|
if (exp>=-51) and (exp<=0) then //can be denormalized
|
|
begin
|
|
mant:=mant shr (-exp);
|
|
exp:=0;
|
|
end
|
|
else
|
|
if (exp<-51) or (exp>2046) then //exponent too large.
|
|
begin
|
|
Result:=0;
|
|
exit;
|
|
end
|
|
else //normalized value
|
|
mant:=mant shl 1; //hide most significant bit
|
|
end;
|
|
end;
|
|
d:=word(exp);
|
|
d:=d shl 52;
|
|
|
|
mant:=mant shr 12;
|
|
d:=d or mant;
|
|
if sign then d:=d or $8000000000000000;
|
|
Result:=pdouble(@d)^;
|
|
end;
|
|
{$ENDIF}
|
|
{$endif}
|
|
|
|
function ReadInt(ValueType: TValueType): Int64;
|
|
begin
|
|
case ValueType of
|
|
vaInt8: Result := ShortInt(Input.ReadByte);
|
|
vaInt16: Result := SmallInt(ReadWord);
|
|
vaInt32: Result := LongInt(ReadDWord);
|
|
vaInt64: Result := Int64(ReadQWord);
|
|
end;
|
|
end;
|
|
|
|
function ReadInt: Int64;
|
|
begin
|
|
Result := ReadInt(TValueType(Input.ReadByte));
|
|
end;
|
|
|
|
{$ifndef FPUNONE}
|
|
function ReadExtended : extended;
|
|
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
|
|
var ext : array[0..9] of byte;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
|
|
Input.ReadBuffer(ext[0],10);
|
|
Result:=ExtendedToDouble(@(ext[0]));
|
|
{$ELSE}
|
|
Input.ReadBuffer(Result,sizeof(Result));
|
|
{$ENDIF}
|
|
end;
|
|
{$endif}
|
|
|
|
function ReadSStr: String;
|
|
var
|
|
len: Byte;
|
|
begin
|
|
len := Input.ReadByte;
|
|
SetLength(Result, len);
|
|
if (len > 0) then
|
|
Input.ReadBuffer(Result[1], len);
|
|
end;
|
|
|
|
function ReadLStr: String;
|
|
var
|
|
len: DWord;
|
|
begin
|
|
len := ReadDWord;
|
|
SetLength(Result, len);
|
|
if (len > 0) then
|
|
Input.ReadBuffer(Result[1], len);
|
|
end;
|
|
|
|
function ReadWStr: WideString;
|
|
var
|
|
len: DWord;
|
|
{$IFDEF ENDIAN_BIG}
|
|
i : integer;
|
|
{$ENDIF}
|
|
begin
|
|
len := ReadDWord;
|
|
SetLength(Result, len);
|
|
if (len > 0) then
|
|
begin
|
|
Input.ReadBuffer(Pointer(@Result[1])^, len*2);
|
|
{$IFDEF ENDIAN_BIG}
|
|
for i:=1 to len do
|
|
Result[i]:=widechar(SwapEndian(word(Result[i])));
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
function ReadUStr: UnicodeString;
|
|
var
|
|
len: DWord;
|
|
{$IFDEF ENDIAN_BIG}
|
|
i : integer;
|
|
{$ENDIF}
|
|
begin
|
|
len := ReadDWord;
|
|
SetLength(Result, len);
|
|
if (len > 0) then
|
|
begin
|
|
Input.ReadBuffer(Pointer(@Result[1])^, len*2);
|
|
{$IFDEF ENDIAN_BIG}
|
|
for i:=1 to len do
|
|
Result[i]:=widechar(SwapEndian(word(Result[i])));
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
procedure ReadPropList(indent: String);
|
|
|
|
procedure ProcessValue(ValueType: TValueType; Indent: String);
|
|
|
|
procedure ProcessBinary;
|
|
var
|
|
ToDo, DoNow, i: LongInt;
|
|
lbuf: array[0..31] of Byte;
|
|
s: String;
|
|
begin
|
|
ToDo := ReadDWord;
|
|
OutLn('{');
|
|
while ToDo > 0 do begin
|
|
DoNow := ToDo;
|
|
if DoNow > 32 then DoNow := 32;
|
|
Dec(ToDo, DoNow);
|
|
s := Indent + ' ';
|
|
Input.ReadBuffer(lbuf, DoNow);
|
|
for i := 0 to DoNow - 1 do
|
|
s := s + IntToHex(lbuf[i], 2);
|
|
OutLn(s);
|
|
end;
|
|
OutLn(indent + '}');
|
|
end;
|
|
|
|
var
|
|
s: String;
|
|
{ len: LongInt; }
|
|
IsFirst: Boolean;
|
|
{$ifndef FPUNONE}
|
|
ext: Extended;
|
|
{$endif}
|
|
|
|
begin
|
|
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: OutLn(IntToStr(ShortInt(Input.ReadByte)));
|
|
vaInt16: OutLn( IntToStr(SmallInt(ReadWord)));
|
|
vaInt32: OutLn(IntToStr(LongInt(ReadDWord)));
|
|
vaInt64: OutLn(IntToStr(Int64(ReadQWord)));
|
|
{$ifndef FPUNONE}
|
|
vaExtended: begin
|
|
ext:=ReadExtended;
|
|
Str(ext,S);// Do not use localized strings.
|
|
OutLn(S);
|
|
end;
|
|
{$endif}
|
|
vaString: begin
|
|
OutString(ReadSStr);
|
|
OutLn('');
|
|
end;
|
|
vaIdent: OutLn(ReadSStr);
|
|
vaFalse: OutLn('False');
|
|
vaTrue: OutLn('True');
|
|
vaBinary: ProcessBinary;
|
|
vaSet: begin
|
|
OutStr('[');
|
|
IsFirst := True;
|
|
while True do begin
|
|
s := ReadSStr;
|
|
if Length(s) = 0 then break;
|
|
if not IsFirst then OutStr(', ');
|
|
IsFirst := False;
|
|
OutStr(s);
|
|
end;
|
|
OutLn(']');
|
|
end;
|
|
vaLString:
|
|
begin
|
|
OutString(ReadLStr);
|
|
OutLn('');
|
|
end;
|
|
vaWString:
|
|
begin
|
|
OutWString(ReadWStr);
|
|
OutLn('');
|
|
end;
|
|
vaUString:
|
|
begin
|
|
OutWString(ReadWStr);
|
|
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 OutLn('!!Single!!'); exit end;
|
|
vaCurrency: begin OutLn('!!Currency!!'); exit end;
|
|
vaDate: begin OutLn('!!Date!!'); exit end;}
|
|
vaUTF8String: begin
|
|
OutUtf8Str(ReadLStr);
|
|
OutLn('');
|
|
end;
|
|
else
|
|
Raise EReadError.CreateFmt(SErrInvalidPropertyType,[Ord(ValueType)]);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
while Input.ReadByte <> 0 do begin
|
|
Input.Seek(-1, soFromCurrent);
|
|
OutStr(indent + ReadSStr + ' = ');
|
|
ProcessValue(TValueType(Input.ReadByte), Indent);
|
|
end;
|
|
end;
|
|
|
|
procedure ReadObject(indent: String);
|
|
var
|
|
b: Byte;
|
|
ObjClassName, ObjName: String;
|
|
ChildPos: LongInt;
|
|
begin
|
|
// Check for FilerFlags
|
|
b := Input.ReadByte;
|
|
if (b and $f0) = $f0 then begin
|
|
if (b and 2) <> 0 then ChildPos := ReadInt;
|
|
end else begin
|
|
b := 0;
|
|
Input.Seek(-1, soFromCurrent);
|
|
end;
|
|
|
|
ObjClassName := ReadSStr;
|
|
ObjName := ReadSStr;
|
|
|
|
OutStr(Indent);
|
|
if (b and 1) <> 0 then OutStr('inherited')
|
|
else
|
|
if (b and 4) <> 0 then OutStr('inline')
|
|
else OutStr('object');
|
|
OutStr(' ');
|
|
if ObjName <> '' then
|
|
OutStr(ObjName + ': ');
|
|
OutStr(ObjClassName);
|
|
if (b and 2) <> 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;
|
|
|
|
type
|
|
PLongWord = ^LongWord;
|
|
const
|
|
signature: PChar = 'TPF0';
|
|
|
|
begin
|
|
if Input.ReadDWord <> PLongWord(Pointer(signature))^ then
|
|
raise EReadError.Create('Illegal stream image' {###SInvalidImage});
|
|
ReadObject('');
|
|
end;
|
|
|
|
|
|
procedure ObjectTextToBinary(Input, Output: TStream);
|
|
var
|
|
parser: TParser;
|
|
|
|
procedure WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
w:=NtoLE(w);
|
|
Output.WriteWord(w);
|
|
end;
|
|
|
|
procedure WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
lw:=NtoLE(lw);
|
|
Output.WriteDWord(lw);
|
|
end;
|
|
|
|
procedure WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
|
|
begin
|
|
qw:=NtoLE(qw);
|
|
Output.WriteBuffer(qw,sizeof(qword));
|
|
end;
|
|
|
|
{$ifndef FPUNONE}
|
|
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
|
|
procedure DoubleToExtended(d : double; e : pointer);
|
|
var mant : qword;
|
|
exp : smallint;
|
|
sign : boolean;
|
|
begin
|
|
mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
|
|
exp :=(qword(d) shr 52) and $7FF;
|
|
sign:=(qword(d) and $8000000000000000)<>0;
|
|
case exp of
|
|
0 : begin
|
|
if mant<>0 then //denormalized value: hidden bit is 0. normalize it
|
|
begin
|
|
exp:=16383-1022;
|
|
while (mant and $8000000000000000)=0 do
|
|
begin
|
|
dec(exp);
|
|
mant:=mant shl 1;
|
|
end;
|
|
dec(exp); //don't shift, most significant bit is not hidden in extended
|
|
end;
|
|
end;
|
|
2047 : exp:=$7FFF //either infinity or NaN
|
|
else
|
|
begin
|
|
inc(exp,16383-1023);
|
|
mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
|
|
end;
|
|
end;
|
|
if sign then exp:=exp or $8000;
|
|
mant:=NtoLE(mant);
|
|
exp:=NtoLE(word(exp));
|
|
move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
|
|
move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure WriteExtended(e : extended);
|
|
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
|
|
var ext : array[0..9] of byte;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
|
|
DoubleToExtended(e,@(ext[0]));
|
|
Output.WriteBuffer(ext[0],10);
|
|
{$ELSE}
|
|
Output.WriteBuffer(e,sizeof(e));
|
|
{$ENDIF}
|
|
end;
|
|
{$endif}
|
|
|
|
procedure WriteString(s: String);
|
|
var size : byte;
|
|
begin
|
|
if length(s)>255 then size:=255
|
|
else size:=length(s);
|
|
Output.WriteByte(size);
|
|
if Length(s) > 0 then
|
|
Output.WriteBuffer(s[1], size);
|
|
end;
|
|
|
|
procedure WriteLString(Const s: String);
|
|
begin
|
|
WriteDWord(Length(s));
|
|
if Length(s) > 0 then
|
|
Output.WriteBuffer(s[1], Length(s));
|
|
end;
|
|
|
|
procedure WriteWString(Const s: WideString);
|
|
var len : longword;
|
|
{$IFDEF ENDIAN_BIG}
|
|
i : integer;
|
|
ws : widestring;
|
|
{$ENDIF}
|
|
begin
|
|
len:=Length(s);
|
|
WriteDWord(len);
|
|
if len > 0 then
|
|
begin
|
|
{$IFDEF ENDIAN_BIG}
|
|
setlength(ws,len);
|
|
for i:=1 to len do
|
|
ws[i]:=widechar(SwapEndian(word(s[i])));
|
|
Output.WriteBuffer(ws[1], len*sizeof(widechar));
|
|
{$ELSE}
|
|
Output.WriteBuffer(s[1], len*sizeof(widechar));
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
procedure WriteInteger(value: Int64);
|
|
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));
|
|
WriteWord(word(value));
|
|
end else if (value >= -2147483648) and (value <= 2147483647) then begin
|
|
Output.WriteByte(Ord(vaInt32));
|
|
WriteDWord(longword(value));
|
|
end else begin
|
|
Output.WriteByte(ord(vaInt64));
|
|
WriteQWord(qword(value));
|
|
end;
|
|
end;
|
|
|
|
procedure ProcessWideString(const left : widestring);
|
|
var ws : widestring;
|
|
begin
|
|
ws:=left+parser.TokenWideString;
|
|
while parser.NextToken = '+' do
|
|
begin
|
|
parser.NextToken; // Get next string fragment
|
|
if not (parser.Token in [toString,toWString]) then
|
|
parser.CheckToken(toWString);
|
|
ws:=ws+parser.TokenWideString;
|
|
end;
|
|
Output.WriteByte(Ord(vaWstring));
|
|
WriteWString(ws);
|
|
end;
|
|
|
|
procedure ProcessProperty; forward;
|
|
|
|
procedure ProcessValue;
|
|
var
|
|
{$ifndef FPUNONE}
|
|
flt: Extended;
|
|
{$endif}
|
|
s: String;
|
|
stream: TMemoryStream;
|
|
begin
|
|
case parser.Token of
|
|
toInteger:
|
|
begin
|
|
WriteInteger(parser.TokenInt);
|
|
parser.NextToken;
|
|
end;
|
|
{$ifndef FPUNONE}
|
|
toFloat:
|
|
begin
|
|
Output.WriteByte(Ord(vaExtended));
|
|
flt := Parser.TokenFloat;
|
|
WriteExtended(flt);
|
|
parser.NextToken;
|
|
end;
|
|
{$endif}
|
|
toString:
|
|
begin
|
|
s := parser.TokenString;
|
|
while parser.NextToken = '+' do
|
|
begin
|
|
parser.NextToken; // Get next string fragment
|
|
case parser.Token of
|
|
toString : s:=s+parser.TokenString;
|
|
toWString : begin
|
|
ProcessWideString(s);
|
|
exit;
|
|
end
|
|
else parser.CheckToken(toString);
|
|
end;
|
|
end;
|
|
if (length(S)>255) then
|
|
begin
|
|
Output.WriteByte(Ord(vaLString));
|
|
WriteLString(S);
|
|
end
|
|
else
|
|
begin
|
|
Output.WriteByte(Ord(vaString));
|
|
WriteString(s);
|
|
end;
|
|
end;
|
|
toWString:
|
|
ProcessWideString('');
|
|
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));
|
|
WriteString(parser.TokenComponentIdent);
|
|
end;
|
|
Parser.NextToken;
|
|
end;
|
|
// Set
|
|
'[':
|
|
begin
|
|
parser.NextToken;
|
|
Output.WriteByte(Ord(vaSet));
|
|
if parser.Token <> ']' then
|
|
while True do
|
|
begin
|
|
parser.CheckToken(toSymbol);
|
|
WriteString(parser.TokenString);
|
|
parser.NextToken;
|
|
if parser.Token = ']' then
|
|
break;
|
|
parser.CheckToken(',');
|
|
parser.NextToken;
|
|
end;
|
|
Output.WriteByte(0);
|
|
parser.NextToken;
|
|
end;
|
|
// List
|
|
'(':
|
|
begin
|
|
parser.NextToken;
|
|
Output.WriteByte(Ord(vaList));
|
|
while parser.Token <> ')' do
|
|
ProcessValue;
|
|
Output.WriteByte(0);
|
|
parser.NextToken;
|
|
end;
|
|
// Collection
|
|
'<':
|
|
begin
|
|
parser.NextToken;
|
|
Output.WriteByte(Ord(vaCollection));
|
|
while parser.Token <> '>' do
|
|
begin
|
|
parser.CheckTokenSymbol('item');
|
|
parser.NextToken;
|
|
// ConvertOrder
|
|
Output.WriteByte(Ord(vaList));
|
|
while not parser.TokenSymbolIs('end') do
|
|
ProcessProperty;
|
|
parser.NextToken; // Skip 'end'
|
|
Output.WriteByte(0);
|
|
end;
|
|
Output.WriteByte(0);
|
|
parser.NextToken;
|
|
end;
|
|
// Binary data
|
|
'{':
|
|
begin
|
|
Output.WriteByte(Ord(vaBinary));
|
|
stream := TMemoryStream.Create;
|
|
try
|
|
parser.HexToBinary(stream);
|
|
WriteDWord(stream.Size);
|
|
Output.WriteBuffer(Stream.Memory^, stream.Size);
|
|
finally
|
|
stream.Free;
|
|
end;
|
|
parser.NextToken;
|
|
end;
|
|
else
|
|
parser.Error(SInvalidProperty);
|
|
end;
|
|
end;
|
|
|
|
procedure ProcessProperty;
|
|
var
|
|
name: String;
|
|
begin
|
|
// Get name of property
|
|
parser.CheckToken(toSymbol);
|
|
name := parser.TokenString;
|
|
while True do begin
|
|
parser.NextToken;
|
|
if parser.Token <> '.' then break;
|
|
parser.NextToken;
|
|
parser.CheckToken(toSymbol);
|
|
name := name + '.' + parser.TokenString;
|
|
end;
|
|
WriteString(name);
|
|
parser.CheckToken('=');
|
|
parser.NextToken;
|
|
ProcessValue;
|
|
end;
|
|
|
|
procedure ProcessObject;
|
|
var
|
|
Flags: Byte;
|
|
ObjectName, ObjectType: String;
|
|
ChildPos: Integer;
|
|
begin
|
|
if parser.TokenSymbolIs('OBJECT') then
|
|
Flags :=0 { IsInherited := False }
|
|
else begin
|
|
if parser.TokenSymbolIs('INHERITED') then
|
|
Flags := 1 { IsInherited := True; }
|
|
else begin
|
|
parser.CheckTokenSymbol('INLINE');
|
|
Flags := 4;
|
|
end;
|
|
end;
|
|
parser.NextToken;
|
|
parser.CheckToken(toSymbol);
|
|
ObjectName := '';
|
|
ObjectType := parser.TokenString;
|
|
parser.NextToken;
|
|
if parser.Token = ':' then begin
|
|
parser.NextToken;
|
|
parser.CheckToken(toSymbol);
|
|
ObjectName := ObjectType;
|
|
ObjectType := parser.TokenString;
|
|
parser.NextToken;
|
|
if parser.Token = '[' then begin
|
|
parser.NextToken;
|
|
ChildPos := parser.TokenInt;
|
|
parser.NextToken;
|
|
parser.CheckToken(']');
|
|
parser.NextToken;
|
|
Flags := Flags or 2;
|
|
end;
|
|
end;
|
|
if Flags <> 0 then begin
|
|
Output.WriteByte($f0 or Flags);
|
|
if (Flags and 2) <> 0 then
|
|
WriteInteger(ChildPos);
|
|
end;
|
|
WriteString(ObjectType);
|
|
WriteString(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;
|
|
parser.NextToken; // Skip end token
|
|
Output.WriteByte(0); // Terminate property list
|
|
end;
|
|
|
|
const
|
|
signature: PChar = 'TPF0';
|
|
begin
|
|
parser := TParser.Create(Input);
|
|
try
|
|
Output.WriteBuffer(signature[0], 4);
|
|
ProcessObject;
|
|
finally
|
|
parser.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure ObjectResourceToText(Input, Output: TStream);
|
|
begin
|
|
Input.ReadResHeader;
|
|
ObjectBinaryToText(Input, Output);
|
|
end;
|
|
|
|
|
|
procedure ObjectTextToResource(Input, Output: TStream);
|
|
var
|
|
StartPos, FixupInfo: LongInt;
|
|
parser: TParser;
|
|
name: String;
|
|
begin
|
|
// Get form type name
|
|
StartPos := Input.Position;
|
|
parser := TParser.Create(Input);
|
|
try
|
|
if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
|
|
parser.NextToken;
|
|
parser.CheckToken(toSymbol);
|
|
parser.NextToken;
|
|
parser.CheckToken(':');
|
|
parser.NextToken;
|
|
parser.CheckToken(toSymbol);
|
|
name := parser.TokenString;
|
|
finally
|
|
parser.Free;
|
|
Input.Position := StartPos;
|
|
end;
|
|
|
|
name := UpperCase(name);
|
|
Output.WriteResourceHeader(name,FixupInfo); // Write resource header
|
|
ObjectTextToBinary(Input, Output); // Convert the stuff!
|
|
Output.FixupResourceHeader(FixupInfo); // Insert real resource data size
|
|
end;
|
|
|
|
|
|
|
|
{ Utility routines }
|
|
|
|
function LineStart(Buffer, BufPos: PChar): PChar;
|
|
|
|
begin
|
|
Result := BufPos;
|
|
while Result > Buffer do begin
|
|
Dec(Result);
|
|
if Result[0] = #10 then break;
|
|
end;
|
|
end;
|
|
|
|
procedure CommonInit;
|
|
begin
|
|
InitCriticalSection(SynchronizeCritSect);
|
|
ExecuteEvent:=RtlEventCreate;
|
|
SynchronizeTimeoutEvent:=RtlEventCreate;
|
|
DoSynchronizeMethod:=false;
|
|
MainThreadID:=GetCurrentThreadID;
|
|
InitCriticalsection(ResolveSection);
|
|
InitHandlerList:=Nil;
|
|
FindGlobalComponentList:=nil;
|
|
IntConstList := TThreadList.Create;
|
|
ClassList := TThreadList.Create;
|
|
ClassAliasList := TStringList.Create;
|
|
{ on unix this maps to a simple rw synchornizer }
|
|
GlobalNameSpace := TMultiReadExclusiveWriteSynchronizer.Create;
|
|
RegisterInitComponentHandler(TComponent,@DefaultInitHandler);
|
|
end;
|
|
|
|
procedure CommonCleanup;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
GlobalNameSpace.BeginWrite;
|
|
with IntConstList.LockList do
|
|
try
|
|
for i := 0 to Count - 1 do
|
|
TIntConst(Items[I]).Free;
|
|
finally
|
|
IntConstList.UnlockList;
|
|
end;
|
|
IntConstList.Free;
|
|
ClassList.Free;
|
|
ClassAliasList.Free;
|
|
RemoveFixupReferences(nil, '');
|
|
DoneCriticalsection(ResolveSection);
|
|
GlobalLists.Free;
|
|
ComponentPages.Free;
|
|
FreeAndNil(NeedResolving);
|
|
{ GlobalNameSpace is an interface so this is enough }
|
|
GlobalNameSpace:=nil;
|
|
|
|
if (InitHandlerList<>Nil) then
|
|
for i := 0 to InitHandlerList.Count - 1 do
|
|
TInitHandler(InitHandlerList.Items[I]).Free;
|
|
InitHandlerList.Free;
|
|
InitHandlerList:=Nil;
|
|
FindGlobalComponentList.Free;
|
|
FindGlobalComponentList:=nil;
|
|
DoneCriticalSection(SynchronizeCritSect);
|
|
RtlEventDestroy(ExecuteEvent);
|
|
RtlEventDestroy(SynchronizeTimeoutEvent);
|
|
end;
|
|
|
|
{ TFiler implementation }
|
|
{$i filer.inc}
|
|
|
|
{ TReader implementation }
|
|
{$i reader.inc}
|
|
|
|
{ TWriter implementations }
|
|
{$i writer.inc}
|
|
{$i twriter.inc}
|
|
|
|
|