home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1997 April
/
Chip_1997-04_cd.bin
/
prezent
/
cb
/
data.z
/
CLASSES.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1997-01-16
|
143KB
|
5,711 lines
{*******************************************************}
{ }
{ Delphi Visual Component Library }
{ }
{ Copyright (c) 1995-1997 Borland International }
{ }
{*******************************************************}
unit Classes; // $Revision: 1.4 $
{$R-}
interface
uses SysUtils, Windows;
const
{ Maximum TList size }
MaxListSize = Maxint div 16;
{ TStream seek origins }
soFromBeginning = 0;
soFromCurrent = 1;
soFromEnd = 2;
{ TFileStream create mode }
fmCreate = $FFFF;
{ TParser special tokens }
toEOF = Char(0);
toSymbol = Char(1);
toString = Char(2);
toInteger = Char(3);
toFloat = Char(4);
type
{ Text alignment types }
TAlignment = (taLeftJustify, taRightJustify, taCenter);
TLeftRight = taLeftJustify..taRightJustify;
{ Types used by standard events }
TShiftState = set of (ssShift, ssAlt, ssCtrl,
ssLeft, ssRight, ssMiddle, ssDouble);
THelpContext = -MaxLongint..MaxLongint;
{ Standard events }
TNotifyEvent = procedure(Sender: TObject) of object;
THelpEvent = function (Command: Word; Data: Longint;
var CallHelp: Boolean): Boolean of object;
TGetStrProc = procedure(const S: string) of object;
{ Exception classes }
EStreamError = class(Exception);
EFCreateError = class(EStreamError);
EFOpenError = class(EStreamError);
EFilerError = class(EStreamError);
EReadError = class(EFilerError);
EWriteError = class(EFilerError);
EClassNotFound = class(EFilerError);
EMethodNotFound = class(EFilerError);
EInvalidImage = class(EFilerError);
EResNotFound = class(Exception);
EListError = class(Exception);
EBitsError = class(Exception);
EStringListError = class(Exception);
EComponentError = class(Exception);
EParserError = class(Exception);
{ Forward class declarations }
TStream = class;
TFiler = class;
TReader = class;
TWriter = class;
TComponent = class;
{ TList class }
PPointerList = ^TPointerList;
TPointerList = array[0..MaxListSize - 1] of Pointer;
TListSortCompare = function (Item1, Item2: Pointer): Integer;
TList = class(TObject)
private
FList: PPointerList;
FCount: Integer;
FCapacity: Integer;
protected
procedure Error; virtual;
function Get(Index: Integer): Pointer;
procedure Grow; virtual;
procedure Put(Index: Integer; Item: Pointer);
procedure SetCapacity(NewCapacity: Integer);
procedure SetCount(NewCount: Integer);
public
destructor Destroy; override;
function Add(Item: Pointer): Integer;
procedure Clear;
procedure Delete(Index: Integer);
procedure Exchange(Index1, Index2: Integer);
function Expand: TList;
function First: Pointer;
function IndexOf(Item: Pointer): Integer;
procedure Insert(Index: Integer; Item: Pointer);
function Last: Pointer;
procedure Move(CurIndex, NewIndex: Integer);
function Remove(Item: Pointer): Integer;
procedure Pack;
procedure Sort(Compare: TListSortCompare);
property Capacity: Integer read FCapacity write SetCapacity;
property Count: Integer read FCount write SetCount;
property Items[Index: Integer]: Pointer read Get write Put; default;
property List: PPointerList read FList;
end;
{ TBits class }
TBits = class
private
FSize: Integer;
FBits: Pointer;
procedure Error;
procedure SetSize(Value: Integer);
procedure SetBit(Index: Integer; Value: Boolean);
function GetBit(Index: Integer): Boolean;
public
destructor Destroy; override;
function OpenBit: Integer;
property Bits[Index: Integer]: Boolean read GetBit write SetBit; default;
property Size: Integer read FSize write SetSize;
end;
{ TPersistent abstract class }
{$M+}
TPersistent = class(TObject)
private
procedure AssignError(Source: TPersistent);
protected
procedure AssignTo(Dest: TPersistent); virtual;
procedure DefineProperties(Filer: TFiler); virtual;
public
procedure Assign(Source: TPersistent); virtual;
end;
{$M-}
{ TPersistent class reference type }
TPersistentClass = class of TPersistent;
{ TCollection class }
TCollection = class;
TCollectionItem = class(TPersistent)
private
FCollection: TCollection;
function GetIndex: Integer;
procedure SetCollection(Value: TCollection);
protected
procedure Changed(AllItems: Boolean);
procedure SetIndex(Value: Integer); virtual;
public
constructor Create(Collection: TCollection); virtual;
destructor Destroy; override;
property Collection: TCollection read FCollection write SetCollection;
property Index: Integer read GetIndex write SetIndex;
end;
TCollectionItemClass = class of TCollectionItem;
TCollection = class(TPersistent)
private
FItemClass: TCollectionItemClass;
FItems: TList;
FUpdateCount: Integer;
function GetCount: Integer;
procedure InsertItem(Item: TCollectionItem);
procedure RemoveItem(Item: TCollectionItem);
protected
procedure Changed;
function GetItem(Index: Integer): TCollectionItem;
procedure SetItem(Index: Integer; Value: TCollectionItem);
procedure Update(Item: TCollectionItem); virtual;
public
constructor Create(ItemClass: TCollectionItemClass);
destructor Destroy; override;
function Add: TCollectionItem;
procedure Assign(Source: TPersistent); override;
procedure BeginUpdate;
procedure Clear;
procedure EndUpdate;
property Count: Integer read GetCount;
property Items[Index: Integer]: TCollectionItem read GetItem write SetItem;
end;
{ TStrings class }
TStrings = class(TPersistent)
private
FUpdateCount: Integer;
function GetCommaText: string;
function GetName(Index: Integer): string;
function GetValue(const Name: string): string;
procedure ReadData(Reader: TReader);
procedure SetCommaText(const Value: string);
procedure SetValue(const Name, Value: string);
procedure WriteData(Writer: TWriter);
protected
procedure DefineProperties(Filer: TFiler); override;
function Get(Index: Integer): string; virtual; abstract;
function GetCount: Integer; virtual; abstract;
function GetObject(Index: Integer): TObject; virtual;
function GetTextStr: string; virtual;
procedure Put(Index: Integer; const S: string); virtual;
procedure PutObject(Index: Integer; AObject: TObject); virtual;
procedure SetTextStr(const Value: string); virtual;
procedure SetUpdateState(Updating: Boolean); virtual;
public
function Add(const S: string): Integer; virtual;
function AddObject(const S: string; AObject: TObject): Integer; virtual;
procedure Append(const S: string);
procedure AddStrings(Strings: TStrings); virtual;
procedure Assign(Source: TPersistent); override;
procedure BeginUpdate;
procedure Clear; virtual; abstract;
procedure Delete(Index: Integer); virtual; abstract;
procedure EndUpdate;
function Equals(Strings: TStrings): Boolean;
procedure Exchange(Index1, Index2: Integer); virtual;
function GetText: PChar; virtual;
function IndexOf(const S: string): Integer; virtual;
function IndexOfName(const Name: string): Integer;
function IndexOfObject(AObject: TObject): Integer;
procedure Insert(Index: Integer; const S: string); virtual; abstract;
procedure InsertObject(Index: Integer; const S: string;
AObject: TObject);
procedure LoadFromFile(const FileName: string); virtual;
procedure LoadFromStream(Stream: TStream); virtual;
procedure Move(CurIndex, NewIndex: Integer); virtual;
procedure SaveToFile(const FileName: string); virtual;
procedure SaveToStream(Stream: TStream); virtual;
procedure SetText(Text: PChar); virtual;
property CommaText: string read GetCommaText write SetCommaText;
property Count: Integer read GetCount;
property Names[Index: Integer]: string read GetName;
property Objects[Index: Integer]: TObject read GetObject write PutObject;
property Values[const Name: string]: string read GetValue write SetValue;
property Strings[Index: Integer]: string read Get write Put; default;
property Text: string read GetTextStr write SetTextStr;
end;
{ TStringList class }
TDuplicates = (dupIgnore, dupAccept, dupError);
PStringItem = ^TStringItem;
TStringItem = record
FString: string;
FObject: TObject;
end;
PStringItemList = ^TStringItemList;
TStringItemList = array[0..MaxListSize] of TStringItem;
TStringList = class(TStrings)
private
FList: PStringItemList;
FCount: Integer;
FCapacity: Integer;
FSorted: Boolean;
FDuplicates: TDuplicates;
FOnChange: TNotifyEvent;
FOnChanging: TNotifyEvent;
procedure ExchangeItems(Index1, Index2: Integer);
procedure Grow;
procedure QuickSort(L, R: Integer);
procedure InsertItem(Index: Integer; const S: string);
procedure SetCapacity(NewCapacity: Integer);
procedure SetSorted(Value: Boolean);
protected
procedure Changed; virtual;
procedure Changing; virtual;
function Get(Index: Integer): string; override;
function GetCount: Integer; override;
function GetObject(Index: Integer): TObject; override;
procedure Put(Index: Integer; const S: string); override;
procedure PutObject(Index: Integer; AObject: TObject); override;
procedure SetUpdateState(Updating: Boolean); override;
public
destructor Destroy; override;
function Add(const S: string): Integer; override;
procedure Clear; override;
procedure Delete(Index: Integer); override;
procedure Exchange(Index1, Index2: Integer); override;
function Find(const S: string; var Index: Integer): Boolean; virtual;
function IndexOf(const S: string): Integer; override;
procedure Insert(Index: Integer; const S: string); override;
procedure Sort; virtual;
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
property Sorted: Boolean read FSorted write SetSorted;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
end;
{ TStream abstract class }
TStream = class(TObject)
private
function GetPosition: Longint;
procedure SetPosition(Pos: Longint);
function GetSize: Longint;
public
function Read(var Buffer; Count: Longint): Longint; virtual; abstract;
function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;
procedure ReadBuffer(var Buffer; Count: Longint);
procedure WriteBuffer(const Buffer; Count: Longint);
function CopyFrom(Source: TStream; Count: Longint): Longint;
function ReadComponent(Instance: TComponent): TComponent;
function ReadComponentRes(Instance: TComponent): TComponent;
procedure WriteComponent(Instance: TComponent);
procedure WriteComponentRes(const ResName: string; Instance: TComponent);
procedure WriteDescendent(Instance, Ancestor: TComponent);
procedure WriteDescendentRes(const ResName: string; Instance, Ancestor: TComponent);
procedure ReadResHeader;
property Position: Longint read GetPosition write SetPosition;
property Size: Longint read GetSize;
end;
{ THandleStream class }
THandleStream = class(TStream)
private
FHandle: Integer;
public
constructor Create(AHandle: Integer);
function Read(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
property Handle: Integer read FHandle;
end;
{ TFileStream class }
TFileStream = class(THandleStream)
public
constructor Create(const FileName: string; Mode: Word);
destructor Destroy; override;
end;
{ TCustomMemoryStream abstract class }
TCustomMemoryStream = class(TStream)
private
FMemory: Pointer;
FSize, FPosition: Longint;
protected
procedure SetPointer(Ptr: Pointer; Size: Longint);
public
function Read(var Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
procedure SaveToStream(Stream: TStream);
procedure SaveToFile(const FileName: string);
property Memory: Pointer read FMemory;
end;
{ TMemoryStream }
TMemoryStream = class(TCustomMemoryStream)
private
FCapacity: Longint;
procedure SetCapacity(NewCapacity: Longint);
protected
function Realloc(var NewCapacity: Longint): Pointer; virtual;
property Capacity: Longint read FCapacity write SetCapacity;
public
destructor Destroy; override;
procedure Clear;
procedure LoadFromStream(Stream: TStream);
procedure LoadFromFile(const FileName: string);
procedure SetSize(NewSize: Longint);
function Write(const Buffer; Count: Longint): Longint; override;
end;
{ TResourceStream }
TResourceStream = class(TCustomMemoryStream)
private
HResInfo: HRSRC;
HGlobal: THandle;
procedure Initialize(Instance: THandle; Name, ResType: PChar);
public
constructor Create(Instance: THandle; const ResName: string; ResType: PChar);
constructor CreateFromID(Instance: THandle; ResID: Integer; ResType: PChar);
destructor Destroy; override;
function Write(const Buffer; Count: Longint): Longint; override;
end;
{ TFiler }
TValueType = (vaNull, vaList, vaInt8, vaInt16, vaInt32, vaExtended,
vaString, vaIdent, vaFalse, vaTrue, vaBinary, vaSet, vaLString,
vaNil, vaCollection);
TFilerFlag = (ffInherited, ffChildPos);
TFilerFlags = set of TFilerFlag;
TReaderProc = procedure(Reader: TReader) of object;
TWriterProc = procedure(Writer: TWriter) of object;
TStreamProc = procedure(Stream: TStream) of object;
TFiler = class(TObject)
private
FStream: TStream;
FBuffer: Pointer;
FBufSize: Integer;
FBufPos: Integer;
FBufEnd: Integer;
FRoot: TComponent;
FAncestor: TPersistent;
FIgnoreChildren: Boolean;
public
constructor Create(Stream: TStream; BufSize: Integer);
destructor Destroy; override;
procedure DefineProperty(const Name: string;
ReadData: TReaderProc; WriteData: TWriterProc;
HasData: Boolean); virtual; abstract;
procedure DefineBinaryProperty(const Name: string;
ReadData, WriteData: TStreamProc;
HasData: Boolean); virtual; abstract;
procedure FlushBuffer; virtual; abstract;
property Root: TComponent read FRoot write FRoot;
property Ancestor: TPersistent read FAncestor write FAncestor;
property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren;
end;
{ TReader }
TFindMethodEvent = procedure(Reader: TReader; const MethodName: string;
var Address: Pointer; var Error: Boolean) of object;
TSetNameEvent = procedure(Reader: TReader; Component: TComponent;
var Name: string) of object;
TReadComponentsProc = procedure(Component: TComponent) of object;
TReaderError = procedure(Reader: TReader; const Message: string; var Handled: Boolean) of object;
TReader = class(TFiler)
private
FOwner: TComponent;
FParent: TComponent;
FFixups: TList;
FLoaded: TList;
FOnFindMethod: TFindMethodEvent;
FOnSetName: TSetNameEvent;
FOnError: TReaderError;
FCanHandleExcepts: Boolean;
FPropName: string;
procedure CheckValue(Value: TValueType);
procedure DoFixupReferences;
procedure FreeFixups;
function GetPosition: Longint;
procedure PropertyError;
procedure ReadBuffer;
procedure ReadCollection(Collection: TCollection);
procedure ReadData(Instance: TComponent);
procedure ReadDataInner(Instance: TComponent);
procedure ReadProperty(AInstance: TPersistent);
procedure ReadPropValue(Instance: TPersistent; PropInfo: Pointer);
function ReadSet(SetType: Pointer): Integer;
procedure SetPosition(Value: Longint);
procedure SkipSetBody;
procedure SkipValue;
procedure SkipProperty;
procedure SkipComponent(SkipHeader: Boolean);
protected
function Error(const Message: string): Boolean; virtual;
function FindMethod(Root: TComponent; const MethodName: string): Pointer; virtual;
function NextValue: TValueType;
procedure SetName(Component: TComponent; var Name: string); virtual;
public
destructor Destroy; override;
procedure BeginReferences;
procedure DefineProperty(const Name: string;
ReadData: TReaderProc; WriteData: TWriterProc;
HasData: Boolean); override;
procedure DefineBinaryProperty(const Name: string;
ReadData, WriteData: TStreamProc;
HasData: Boolean); override;
function EndOfList: Boolean;
procedure EndReferences;
procedure FixupReferences;
procedure FlushBuffer; override;
procedure Read(var Buf; Count: Longint);
function ReadBoolean: Boolean;
function ReadChar: Char;
function ReadComponent(Component: TComponent): TComponent;
procedure ReadComponents(AOwner, AParent: TComponent;
Proc: TReadComponentsProc);
function ReadFloat: Extended;
function ReadIdent: string;
function ReadInteger: Longint;
procedure ReadListBegin;
procedure ReadListEnd;
procedure ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer);
function ReadRootComponent(Root: TComponent): TComponent;
procedure ReadSignature;
function ReadStr: string;
function ReadString: string;
function ReadValue: TValueType;
property Owner: TComponent read FOwner write FOwner;
property Parent: TComponent read FParent write FParent;
property Position: Longint read GetPosition write SetPosition;
property OnError: TReaderError read FOnError write FOnError;
property OnFindMethod: TFindMethodEvent read FOnFindMethod write FOnFindMethod;
property OnSetName: TSetNameEvent read FOnSetName write FOnSetName;
end;
{ TWriter }
TWriter = class(TFiler)
private
FRootAncestor: TComponent;
FPropPath: string;
FAncestorList: TList;
FAncestorPos: Integer;
FChildPos: Integer;
procedure AddAncestor(Component: TComponent);
function GetPosition: Longint;
procedure SetPosition(Value: Longint);
procedure WriteBinary(WriteData: TStreamProc);
procedure WriteBuffer;
procedure WriteData(Instance: TComponent); virtual; // linker optimization
procedure WritePrefix(Flags: TFilerFlags; AChildPos: Integer);
procedure WriteProperty(Instance: TPersistent; PropInfo: Pointer);
procedure WriteProperties(Instance: TPersistent);
procedure WritePropName(const PropName: string);
procedure WriteValue(Value: TValueType);
public
destructor Destroy; override;
procedure DefineProperty(const Name: string;
ReadData: TReaderProc; WriteData: TWriterProc;
HasData: Boolean); override;
procedure DefineBinaryProperty(const Name: string;
ReadData, WriteData: TStreamProc;
HasData: Boolean); override;
procedure FlushBuffer; override;
procedure Write(const Buf; Count: Longint);
procedure WriteBoolean(Value: Boolean);
procedure WriteCollection(Value: TCollection);
procedure WriteComponent(Component: TComponent);
procedure WriteChar(Value: Char);
procedure WriteDescendent(Root: TComponent; AAncestor: TComponent);
procedure WriteFloat(Value: Extended);
procedure WriteIdent(const Ident: string);
procedure WriteInteger(Value: Longint);
procedure WriteListBegin;
procedure WriteListEnd;
procedure WriteRootComponent(Root: TComponent);
procedure WriteSignature;
procedure WriteStr(const Value: string);
procedure WriteString(const Value: string);
property Position: Longint read GetPosition write SetPosition;
property RootAncestor: TComponent read FRootAncestor write FRootAncestor;
end;
{ TParser }
TParser = class(TObject)
private
FStream: TStream;
FOrigin: Longint;
FBuffer: PChar;
FBufPtr: PChar;
FBufEnd: PChar;
FSourcePtr: PChar;
FSourceEnd: PChar;
FTokenPtr: PChar;
FStringPtr: PChar;
FSourceLine: Integer;
FSaveChar: Char;
FToken: Char;
procedure ReadBuffer;
procedure SkipBlanks;
public
constructor Create(Stream: TStream);
destructor Destroy; override;
procedure CheckToken(T: Char);
procedure CheckTokenSymbol(const S: string);
procedure Error(Ident: Integer);
procedure ErrorFmt(Ident: Integer; 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: Longint;
function TokenString: string;
function TokenSymbolIs(const S: string): Boolean;
property SourceLine: Integer read FSourceLine;
property Token: Char read FToken;
end;
{ TThread }
EThread = class(Exception);
TThreadMethod = procedure of object;
TThreadPriority = (tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest,
tpTimeCritical);
TThread = class
private
FHandle: THandle;
FThreadID: THandle;
FTerminated: Boolean;
FSuspended: Boolean;
FMainThreadWaiting: Boolean;
FFreeOnTerminate: Boolean;
FFinished: Boolean;
FReturnValue: Integer;
FOnTerminate: TNotifyEvent;
FMethod: TThreadMethod;
FSynchronizeException: TObject;
procedure CallOnTerminate;
function GetPriority: TThreadPriority;
procedure SetPriority(Value: TThreadPriority);
procedure SetSuspended(Value: Boolean);
protected
procedure DoTerminate; virtual;
procedure Execute; virtual; abstract;
procedure Synchronize(Method: TThreadMethod);
property ReturnValue: Integer read FReturnValue write FReturnValue;
property Terminated: Boolean read FTerminated;
public
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
procedure Resume;
procedure Suspend;
procedure Terminate;
function WaitFor: Integer;
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Handle: THandle read FHandle;
property Priority: TThreadPriority read GetPriority write SetPriority;
property Suspended: Boolean read FSuspended write SetSuspended;
property ThreadID: THandle read FThreadID;
property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
end;
{ TComponent class }
TOperation = (opInsert, opRemove);
TComponentState = set of (csLoading, csReading, csWriting, csDestroying,
csDesigning, csAncestor, csUpdating, csFixups);
TComponentStyle = set of (csInheritable, csCheckPropAvail);
TGetChildProc = procedure (Child: TComponent) of object;
TComponentName = type string;
TComponent = class(TPersistent)
private
FOwner: TComponent;
FName: TComponentName;
FTag: Longint;
FComponents: TList;
FFreeNotifies: TList;
FDesignInfo: Longint;
FComponentState: TComponentState;
function GetComponent(AIndex: Integer): TComponent;
function GetComponentCount: Integer;
function GetComponentIndex: Integer;
procedure Insert(AComponent: TComponent);
procedure ReadLeft(Reader: TReader);
procedure ReadTop(Reader: TReader);
procedure Remove(AComponent: TComponent);
procedure SetComponentIndex(Value: Integer);
procedure SetReference(Enable: Boolean);
procedure WriteLeft(Writer: TWriter);
procedure WriteTop(Writer: TWriter);
protected
FComponentStyle: TComponentStyle;
procedure ChangeName(const NewName: TComponentName);
procedure DefineProperties(Filer: TFiler); override;
procedure GetChildren(Proc: TGetChildProc); dynamic;
function GetChildOwner: TComponent; dynamic;
function GetChildParent: TComponent; dynamic;
procedure Loaded; virtual;
procedure Notification(AComponent: TComponent;
Operation: TOperation); virtual;
procedure ReadState(Reader: TReader); virtual;
procedure SetAncestor(Value: Boolean);
procedure SetDesigning(Value: Boolean);
procedure SetName(const NewName: TComponentName); virtual;
procedure SetChildOrder(Child: TComponent; Order: Integer); dynamic;
procedure SetParentComponent(Value: TComponent); dynamic;
procedure Updating; dynamic;
procedure Updated; dynamic;
procedure ValidateRename(AComponent: TComponent;
const CurName, NewName: string); virtual;
procedure WriteState(Writer: TWriter); virtual;
public
constructor Create(AOwner: TComponent); virtual;
destructor Destroy; override;
procedure DestroyComponents;
procedure Destroying;
function FindComponent(const AName: string): TComponent;
function GetParentComponent: TComponent; dynamic;
function HasParent: Boolean; dynamic;
procedure FreeNotification(AComponent: TComponent);
procedure InsertComponent(AComponent: TComponent);
procedure RemoveComponent(AComponent: TComponent);
property Components[Index: Integer]: TComponent read GetComponent;
property ComponentCount: Integer read GetComponentCount;
property ComponentIndex: Integer read GetComponentIndex write SetComponentIndex;
property ComponentState: TComponentState read FComponentState;
property ComponentStyle: TComponentStyle read FComponentStyle;
property DesignInfo: Longint read FDesignInfo write FDesignInfo;
property Owner: TComponent read FOwner;
published
property Name: TComponentName read FName write SetName stored False;
property Tag: Longint read FTag write FTag default 0;
end;
{ TComponent class reference type }
TComponentClass = class of TComponent;
{ Component registration handlers }
const
RegisterComponentsProc: procedure(const Page: string;
ComponentClasses: array of TComponentClass) = nil;
RegisterNoIconProc: procedure(ComponentClasses: array of TComponentClass) = nil;
{ Point and rectangle constructors }
function Point(AX, AY: Integer): TPoint;
function SmallPoint(AX, AY: SmallInt): TSmallPoint;
function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
{ Class registration routines }
procedure RegisterClass(AClass: TPersistentClass);
procedure RegisterClasses(AClasses: array of TPersistentClass);
procedure RegisterClassAlias(AClass: TPersistentClass; const Alias: string);
procedure UnRegisterClass(AClass: TPersistentClass);
procedure UnRegisterClasses(AClasses: array of TPersistentClass);
function FindClass(const ClassName: string): TPersistentClass;
function GetClass(const ClassName: string): TPersistentClass;
{ Component registration routines }
procedure RegisterComponents(const Page: string;
ComponentClasses: array of TComponentClass);
procedure RegisterNoIcon(ComponentClasses: array of TComponentClass);
{ Object filing routines }
type
TIdentToInt = function(const Ident: string; var Int: Longint): Boolean;
TIntToIdent = function(Int: Longint; var Ident: string): Boolean;
TFindGlobalComponent = function(const Name: string): TComponent;
var
MainThreadID: THandle;
FindGlobalComponent: TFindGlobalComponent;
procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToInt: TIdentToInt;
IntToIdent: TIntToIdent);
function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
function InitComponentRes(const ResName: string; Instance: TComponent): Boolean;
function ReadComponentRes(const ResName: string; Instance: TComponent): TComponent;
function ReadComponentResFile(const FileName: string; Instance: TComponent): TComponent;
procedure WriteComponentResFile(const FileName: string; Instance: TComponent);
procedure GlobalFixupReferences;
procedure GetFixupReferenceNames(Root: TComponent; Names: TStrings);
procedure GetFixupInstanceNames(Root: TComponent;
const ReferenceRootName: string; Names: TStrings);
procedure RedirectFixupReferences(Root: TComponent; const OldRootName,
NewRootName: string);
procedure RemoveFixupReferences(Root: TComponent; const RootName: string);
procedure BeginGlobalLoading;
procedure NotifyGlobalLoading;
procedure EndGlobalLoading;
function CollectionsEqual(C1, C2: TCollection): Boolean;
{ Object conversion routines }
procedure ObjectBinaryToText(Input, Output: TStream);
procedure ObjectTextToBinary(Input, Output: TStream);
procedure ObjectResourceToText(Input, Output: TStream);
procedure ObjectTextToResource(Input, Output: TStream);
{ Utility routines }
function LineStart(Buffer, BufPos: PChar): PChar;
implementation
uses Consts, TypInfo;
const
FilerSignature: array[1..4] of Char = 'TPF0';
var
ClassList: TList = nil;
ClassAliasList: TStringList = nil;
IntConstList: TList = nil;
type
TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;
{ 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;
{ Class registration routines }
type
PFieldClassTable = ^TFieldClassTable;
TFieldClassTable = packed record
Count: Smallint;
Classes: array[0..8191] of TPersistentClass;
end;
function GetFieldClassTable(AClass: TClass): PFieldClassTable; assembler;
asm
MOV EAX,[EAX-52].Integer
OR EAX,EAX
JE @@1
MOV EAX,[EAX+2].Integer
@@1:
end;
procedure ClassNotFound(const ClassName: string);
begin
raise EClassNotFound.Create(FmtLoadStr(SClassNotFound, [ClassName]));
end;
function GetClass(const ClassName: string): TPersistentClass;
var
I: Integer;
begin
for I := 0 to ClassList.Count - 1 do
begin
Result := ClassList[I];
if Result.ClassNameIs(ClassName) then Exit;
end;
I := ClassAliasList.IndexOf(ClassName);
if I >= 0 then
begin
Result := TPersistentClass(ClassAliasList.Objects[I]);
Exit;
end;
Result := nil;
end;
function FindClass(const ClassName: string): TPersistentClass;
begin
Result := GetClass(ClassName);
if Result = nil then ClassNotFound(ClassName);
end;
function FindFieldClass(Instance: TObject;
const ClassName: string): TPersistentClass;
var
I: Integer;
ClassTable: PFieldClassTable;
ClassType: TClass;
begin
ClassType := Instance.ClassType;
while ClassType <> TPersistent do
begin
ClassTable := GetFieldClassTable(ClassType);
if ClassTable <> nil then
for I := 0 to ClassTable^.Count - 1 do
begin
Result := ClassTable^.Classes[I];
if CompareText(Result.ClassName, ClassName) = 0 then Exit;
end;
ClassType := ClassType.ClassParent;
end;
Result := FindClass(ClassName);
end;
procedure RegisterClass(AClass: TPersistentClass);
var
ClassName: string;
begin
while ClassList.IndexOf(AClass) = -1 do
begin
ClassName := AClass.ClassName;
if GetClass(ClassName) <> nil then
raise EFilerError.CreateResFmt(SDuplicateClass, [ClassName]);
ClassList.Add(AClass);
if AClass = TPersistent then Break;
AClass := TPersistentClass(AClass.ClassParent);
end;
end;
procedure RegisterClasses(AClasses: array of TPersistentClass);
var
I: Integer;
begin
for I := Low(AClasses) to High(AClasses) do RegisterClass(AClasses[I]);
end;
procedure RegisterClassAlias(AClass: TPersistentClass; const Alias: string);
begin
RegisterClass(AClass);
ClassAliasList.AddObject(Alias, TObject(AClass));
end;
procedure UnRegisterClass(AClass: TPersistentClass);
begin
ClassList.Remove(AClass);
end;
procedure UnRegisterClasses(AClasses: array of TPersistentClass);
var
I: Integer;
begin
for I := Low(AClasses) to High(AClasses) do UnRegisterClass(AClasses[I]);
end;
{ Component registration routines }
procedure RegisterComponents(const Page: string;
ComponentClasses: array of TComponentClass);
begin
if Assigned(RegisterComponentsProc) then
RegisterComponentsProc(Page, ComponentClasses)
else
raise EComponentError.CreateRes(SRegisterError);
end;
procedure RegisterNoIcon(ComponentClasses: array of TComponentClass);
begin
if Assigned(RegisterNoIconProc) then
RegisterNoIconProc(ComponentClasses)
else
raise EComponentError.CreateRes(SRegisterError);
end;
{ Component filing }
type
TIntConst = class
IntegerType: PTypeInfo;
IdentToInt: TIdentToInt;
IntToIdent: TIntToIdent;
constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
AIntToIdent: TIntToIdent);
end;
constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
AIntToIdent: TIntToIdent);
begin
IntegerType := AIntegerType;
IdentToInt := AIdentToInt;
IntToIdent := AIntToIdent;
end;
procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToInt: TIdentToInt;
IntToIdent: TIntToIdent);
begin
IntConstList.Add(TIntConst.Create(IntegerType, IdentToInt, IntToIdent));
end;
function InternalReadComponentRes(const ResName: string; var Instance: TComponent): Boolean;
var
HRsrc: THandle;
begin { avoid possible EResNotFound exception }
HRsrc := FindResource(HInstance, PChar(ResName), RT_RCDATA);
Result := HRsrc <> 0;
if not Result then Exit;
FreeResource(HRsrc);
with TResourceStream.Create(HInstance, ResName, RT_RCDATA) do
try
Instance := ReadComponent(Instance);
finally
Free;
end;
Result := True;
end;
var
GlobalLoaded: TList;
GlobalLists: TList;
procedure BeginGlobalLoading;
begin
GlobalLists.Add(GlobalLoaded);
GlobalLoaded := TList.Create;
end;
procedure NotifyGlobalLoading;
var
I: Integer;
begin
for I := 0 to GlobalLoaded.Count - 1 do
TComponent(GlobalLoaded[I]).Loaded;
end;
procedure EndGlobalLoading;
begin
GlobalLoaded.Free;
GlobalLoaded := GlobalLists.Last;
GlobalLists.Delete(GlobalLists.Count - 1);
end;
function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
function InitComponent(ClassType: TClass): Boolean;
begin
Result := False;
if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit;
Result := InitComponent(ClassType.ClassParent);
Result := InternalReadComponentRes(ClassType.ClassName, Instance) or Result;
end;
begin
BeginGlobalLoading;
try
Result := InitComponent(Instance.ClassType);
NotifyGlobalLoading;
finally
EndGlobalLoading;
end;
end;
function InitComponentRes(const ResName: string; Instance: TComponent): Boolean;
begin
Result := InternalReadComponentRes(ResName, Instance);
end;
function ReadComponentRes(const ResName: string; Instance: TComponent): TComponent;
begin
if InternalReadComponentRes(ResName, Instance) then
Result := Instance else
raise EResNotFound.CreateResFmt(SResNotFound, [ResName]);
end;
function ReadComponentResFile(const FileName: string; Instance: TComponent): TComponent;
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
try
Result := Stream.ReadComponentRes(Instance);
finally
Stream.Free;
end;
end;
procedure WriteComponentResFile(const FileName: string; Instance: TComponent);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
Stream.WriteComponentRes(Instance.ClassName, Instance);
finally
Stream.Free;
end;
end;
function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
asm
PUSH ESI
PUSH EDI
MOV ESI,P1
MOV EDI,P2
MOV EDX,ECX
XOR EAX,EAX
AND EDX,3
SHR ECX,2
REPE CMPSD
JNE @@2
MOV ECX,EDX
REPE CMPSB
JNE @@2
@@1: INC EAX
@@2: POP EDI
POP ESI
end;
function StreamsEqual(S1, S2: TMemoryStream): Boolean;
begin
Result := (S1.Size = S2.Size) and CompareMem(S1.Memory, S2.Memory, S1.Size);
end;
function CollectionsEqual(C1, C2: TCollection): Boolean;
var
S1, S2: TMemoryStream;
procedure WriteCollection(Stream: TStream; Collection: TCollection);
var
Writer: TWriter;
begin
Writer := TWriter.Create(Stream, 1024);
try
Writer.WriteCollection(Collection);
finally
Writer.Free;
end;
end;
begin
Result := False;
if C1.ClassType <> C2.ClassType then Exit;
if C1.Count <> C2.Count then Exit;
S1 := TMemoryStream.Create;
try
WriteCollection(S1, C1);
S2 := TMemoryStream.Create;
try
WriteCollection(S2, C2);
Result := StreamsEqual(S1, S2);
finally
S2.Free;
end;
finally
S1.Free;
end;
end;
{ Utility routines }
function LineStart(Buffer, BufPos: PChar): PChar; assembler;
asm
PUSH EDI
MOV EDI,EDX
MOV ECX,EDX
SUB ECX,EAX
SUB ECX,1
JBE @@1
MOV EDX,EAX
DEC EDI
MOV AL,0AH
STD
REPNE SCASB
CLD
MOV EAX,EDX
JNE @@1
LEA EAX,[EDI+2]
@@1: POP EDI
end;
procedure ListError(Ident: Integer);
begin
raise EListError.CreateRes(Ident);
end;
procedure ListIndexError;
begin
ListError(SListIndexError);
end;
{ TList }
destructor TList.Destroy;
begin
Clear;
end;
function TList.Add(Item: Pointer): Integer;
begin
Result := FCount;
if Result = FCapacity then Grow;
FList^[Result] := Item;
Inc(FCount);
end;
procedure TList.Clear;
begin
SetCount(0);
SetCapacity(0);
end;
procedure TList.Delete(Index: Integer);
begin
if (Index < 0) or (Index >= FCount) then Error;
Dec(FCount);
if Index < FCount then
System.Move(FList^[Index + 1], FList^[Index],
(FCount - Index) * SizeOf(Pointer));
end;
procedure TList.Error;
begin
ListIndexError;
end;
procedure TList.Exchange(Index1, Index2: Integer);
var
Item: Pointer;
begin
if (Index1 < 0) or (Index1 >= FCount) or
(Index2 < 0) or (Index2 >= FCount) then Error;
Item := FList^[Index1];
FList^[Index1] := FList^[Index2];
FList^[Index2] := Item;
end;
function TList.Expand: TList;
begin
if FCount = FCapacity then Grow;
Result := Self;
end;
function TList.First: Pointer;
begin
Result := Get(0);
end;
function TList.Get(Index: Integer): Pointer;
begin
if (Index < 0) or (Index >= FCount) then Error;
Result := FList^[Index];
end;
procedure TList.Grow;
var
Delta: Integer;
begin
if FCapacity > 8 then Delta := 16 else
if FCapacity > 4 then Delta := 8 else
Delta := 4;
SetCapacity(FCapacity + Delta);
end;
function TList.IndexOf(Item: Pointer): Integer;
begin
Result := 0;
while (Result < FCount) and (FList^[Result] <> Item) do Inc(Result);
if Result = FCount then Result := -1;
end;
procedure TList.Insert(Index: Integer; Item: Pointer);
begin
if (Index < 0) or (Index > FCount) then Error;
if FCount = FCapacity then Grow;
if Index < FCount then
System.Move(FList^[Index], FList^[Index + 1],
(FCount - Index) * SizeOf(Pointer));
FList^[Index] := Item;
Inc(FCount);
end;
function TList.Last: Pointer;
begin
Result := Get(FCount - 1);
end;
procedure TList.Move(CurIndex, NewIndex: Integer);
var
Item: Pointer;
begin
if CurIndex <> NewIndex then
begin
if (NewIndex < 0) or (NewIndex >= FCount) then Error;
Item := Get(CurIndex);
Delete(CurIndex);
Insert(NewIndex, Item);
end;
end;
procedure TList.Put(Index: Integer; Item: Pointer);
begin
if (Index < 0) or (Index >= FCount) then Error;
FList^[Index] := Item;
end;
function TList.Remove(Item: Pointer): Integer;
begin
Result := IndexOf(Item);
if Result <> -1 then Delete(Result);
end;
procedure TList.Pack;
var
I: Integer;
begin
for I := FCount - 1 downto 0 do if Items[I] = nil then Delete(I);
end;
procedure TList.SetCapacity(NewCapacity: Integer);
begin
if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then Error;
if NewCapacity <> FCapacity then
begin
ReallocMem(FList, NewCapacity * SizeOf(Pointer));
FCapacity := NewCapacity;
end;
end;
procedure TList.SetCount(NewCount: Integer);
begin
if (NewCount < 0) or (NewCount > MaxListSize) then Error;
if NewCount > FCapacity then SetCapacity(NewCount);
if NewCount > FCount then
FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(Pointer), 0);
FCount := NewCount;
end;
procedure QuickSort(SortList: PPointerList; L, R: Integer;
SCompare: TListSortCompare);
var
I, J: Integer;
P, T: Pointer;
begin
repeat
I := L;
J := R;
P := SortList^[(L + R) shr 1];
repeat
while SCompare(SortList^[I], P) < 0 do Inc(I);
while SCompare(SortList^[J], P) > 0 do Dec(J);
if I <= J then
begin
T := SortList^[I];
SortList^[I] := SortList^[J];
SortList^[J] := T;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then QuickSort(SortList, L, J, SCompare);
L := I;
until I >= R;
end;
procedure TList.Sort(Compare: TListSortCompare);
begin
if (FList <> nil) and (Count > 0) then
QuickSort(FList, 0, Count - 1, Compare);
end;
{ TBits }
const
BitsPerInt = SizeOf(Integer) * 8;
type
TBitEnum = 0..BitsPerInt - 1;
TBitSet = set of TBitEnum;
PBitArray = ^TBitArray;
TBitArray = array[0..4096] of TBitSet;
destructor TBits.Destroy;
begin
SetSize(0);
inherited Destroy;
end;
procedure TBits.Error;
begin
raise EBitsError.CreateRes(SBitsIndexError);
end;
procedure TBits.SetSize(Value: Integer);
var
NewMem: Pointer;
NewMemSize: Integer;
OldMemSize: Integer;
function Min(X, Y: Integer): Integer;
begin
Result := X;
if X > Y then Result := Y;
end;
begin
if Value <> Size then
begin
if Value < 0 then Error;
NewMemSize := ((Value + BitsPerInt - 1) div BitsPerInt) * SizeOf(Integer);
OldMemSize := ((Size + BitsPerInt - 1) div BitsPerInt) * SizeOf(Integer);
if NewMemSize <> OldMemSize then
begin
NewMem := nil;
if NewMemSize <> 0 then
begin
GetMem(NewMem, NewMemSize);
FillChar(NewMem^, NewMemSize, 0);
end;
if OldMemSize <> 0 then
begin
if NewMem <> nil then
Move(FBits^, NewMem^, Min(OldMemSize, NewMemSize));
FreeMem(FBits, OldMemSize);
end;
FBits := NewMem;
end;
FSize := Value;
end;
end;
procedure TBits.SetBit(Index: Integer; Value: Boolean); assembler;
asm
CMP Index,[EAX].FSize
JAE @@Size
@@1: MOV EAX,[EAX].FBits
OR Value,Value
JZ @@2
BTS [EAX],Index
RET
@@2: BTR [EAX],Index
RET
@@Size: CMP Index,0
JL TBits.Error
PUSH Self
PUSH Index
PUSH ECX {Value}
INC Index
CALL TBits.SetSize
POP ECX {Value}
POP Index
POP Self
JMP @@1
end;
function TBits.GetBit(Index: Integer): Boolean; assembler;
asm
CMP Index,[EAX].FSize
JAE TBits.Error
MOV EAX,[EAX].FBits
BT [EAX],Index
SBB EAX,EAX
AND EAX,1
end;
function TBits.OpenBit: Integer;
var
I: Integer;
B: TBitSet;
J: TBitEnum;
E: Integer;
begin
E := (Size + BitsPerInt - 1) div BitsPerInt - 1;
for I := 0 to E do
if PBitArray(FBits)^[I] <> [0..BitsPerInt - 1] then
begin
B := PBitArray(FBits)^[I];
for J := Low(J) to High(J) do
begin
if not (J in B) then
begin
Result := I * BitsPerInt + J;
if Result >= Size then Result := Size;
Exit;
end;
end;
end;
Result := Size;
end;
{ TPersistent }
procedure TPersistent.Assign(Source: TPersistent);
begin
if Source <> nil then Source.AssignTo(Self) else AssignError(nil);
end;
procedure TPersistent.AssignError(Source: TPersistent);
var
SourceName: string;
begin
if Source <> nil then
SourceName := Source.ClassName else
SourceName := 'nil';
raise EConvertError.CreateResFmt(SAssignError, [SourceName, ClassName]);
end;
procedure TPersistent.AssignTo(Dest: TPersistent);
begin
Dest.AssignError(Self);
end;
procedure TPersistent.DefineProperties(Filer: TFiler);
begin
end;
{ TCollectionItem }
constructor TCollectionItem.Create(Collection: TCollection);
begin
SetCollection(Collection);
end;
destructor TCollectionItem.Destroy;
begin
SetCollection(nil);
end;
procedure TCollectionItem.Changed(AllItems: Boolean);
var
Item: TCollectionItem;
begin
if (FCollection <> nil) and (FCollection.FUpdateCount = 0) then
begin
if AllItems then Item := nil else Item := Self;
FCollection.Update(Item);
end;
end;
function TCollectionItem.GetIndex: Integer;
begin
if FCollection <> nil then
Result := FCollection.FItems.IndexOf(Self) else
Result := -1;
end;
procedure TCollectionItem.SetCollection(Value: TCollection);
begin
if FCollection <> Value then
begin
if FCollection <> nil then FCollection.RemoveItem(Self);
if Value <> nil then Value.InsertItem(Self);
end;
end;
procedure TCollectionItem.SetIndex(Value: Integer);
var
CurIndex: Integer;
begin
CurIndex := GetIndex;
if (CurIndex >= 0) and (CurIndex <> Value) then
begin
FCollection.FItems.Move(CurIndex, Value);
Changed(True);
end;
end;
{ TCollection }
constructor TCollection.Create(ItemClass: TCollectionItemClass);
begin
FItemClass := ItemClass;
FItems := TList.Create;
end;
destructor TCollection.Destroy;
begin
FUpdateCount := 1;
if FItems <> nil then Clear;
FItems.Free;
end;
function TCollection.Add: TCollectionItem;
begin
Result := FItemClass.Create(Self);
end;
procedure TCollection.Assign(Source: TPersistent);
var
I: Integer;
begin
if Source is TCollection then
begin
BeginUpdate;
try
Clear;
for I := 0 to TCollection(Source).Count - 1 do
Add.Assign(TCollection(Source).Items[I]);
finally
EndUpdate;
end;
Exit;
end;
inherited Assign(Source);
end;
procedure TCollection.BeginUpdate;
begin
Inc(FUpdateCount);
end;
procedure TCollection.Changed;
begin
if FUpdateCount = 0 then Update(nil);
end;
procedure TCollection.Clear;
begin
if FItems.Count > 0 then
begin
BeginUpdate;
try
while FItems.Count > 0 do TCollectionItem(FItems.Last).Free;
finally
EndUpdate;
end;
end;
end;
procedure TCollection.EndUpdate;
begin
Dec(FUpdateCount);
Changed;
end;
function TCollection.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TCollection.GetItem(Index: Integer): TCollectionItem;
begin
Result := FItems[Index];
end;
procedure TCollection.InsertItem(Item: TCollectionItem);
begin
if not (Item is FItemClass) then ListError(SInvalidProperty);
FItems.Add(Item);
Item.FCollection := Self;
Changed;
end;
procedure TCollection.RemoveItem(Item: TCollectionItem);
begin
FItems.Remove(Item);
Item.FCollection := nil;
Changed;
end;
procedure TCollection.SetItem(Index: Integer; Value: TCollectionItem);
begin
TCollectionItem(FItems[Index]).Assign(Value);
end;
procedure TCollection.Update(Item: TCollectionItem);
begin
end;
{ TStrings }
function TStrings.Add(const S: string): Integer;
begin
Result := GetCount;
Insert(Result, S);
end;
function TStrings.AddObject(const S: string; AObject: TObject): Integer;
begin
Result := Add(S);
PutObject(Result, AObject);
end;
procedure TStrings.Append(const S: string);
begin
Add(S);
end;
procedure TStrings.AddStrings(Strings: TStrings);
var
I: Integer;
begin
BeginUpdate;
try
for I := 0 to Strings.Count - 1 do
AddObject(Strings[I], Strings.Objects[I]);
finally
EndUpdate;
end;
end;
procedure TStrings.Assign(Source: TPersistent);
begin
if Source is TStrings then
begin
BeginUpdate;
try
Clear;
AddStrings(TStrings(Source));
finally
EndUpdate;
end;
Exit;
end;
inherited Assign(Source);
end;
procedure TStrings.BeginUpdate;
begin
if FUpdateCount = 0 then SetUpdateState(True);
Inc(FUpdateCount);
end;
procedure TStrings.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then
begin
Result := True;
if Filer.Ancestor is TStrings then
Result := not Equals(TStrings(Filer.Ancestor))
end
else Result := Count > 0;
end;
begin
Filer.DefineProperty('Strings', ReadData, WriteData, DoWrite);
end;
procedure TStrings.EndUpdate;
begin
Dec(FUpdateCount);
if FUpdateCount = 0 then SetUpdateState(False);
end;
function TStrings.Equals(Strings: TStrings): Boolean;
var
I, Count: Integer;
begin
Result := False;
Count := GetCount;
if Count <> Strings.GetCount then Exit;
for I := 0 to Count - 1 do if Get(I) <> Strings.Get(I) then Exit;
Result := True;
end;
procedure TStrings.Exchange(Index1, Index2: Integer);
var
TempObject: TObject;
TempString: string;
begin
TempString := Strings[Index1];
TempObject := Objects[Index1];
Strings[Index1] := Strings[Index2];
Objects[Index1] := Objects[Index2];
Strings[Index2] := TempString;
Objects[Index2] := TempObject;
end;
function TStrings.GetCommaText: string;
var
P, S, T: PChar;
I, L, Count: Integer;
Quotes: Boolean;
Text: array[0..4095] of Char;
begin
Count := GetCount;
if (Count = 1) and (Get(0) = '') then Result := '""' else
begin
T := Text;
for I := 0 to Count - 1 do
begin
if I <> 0 then
begin
T^ := ',';
Inc(T);
end;
S := PChar(Get(I));
L := 0;
Quotes := False;
P := S;
while P^ <> #0 do
begin
if not Quotes and ((P^ <= ' ') or (P^ = '"') or (P^ = ',')) then
begin
Inc(L, 2);
Quotes := True;
end;
if P^ = '"' then Inc(L);
Inc(L);
Inc(P);
end;
if T + L >= Text + SizeOf(Text) then Break;
if Quotes then
begin
T^ := '"';
Inc(T);
end;
P := S;
while P^ <> #0 do
begin
if P^ = '"' then
begin
T^ := '"';
Inc(T);
end;
T^ := P^;
Inc(T);
Inc(P);
end;
if Quotes then
begin
T^ := '"';
Inc(T);
end;
end;
SetString(Result, Text, T - Text);
end;
end;
function TStrings.GetName(Index: Integer): string;
var
P: Integer;
begin
Result := Get(Index);
P := Pos('=', Result);
if P <> 0 then
SetLength(Result, P-1) else
SetLength(Result, 0);
end;
function TStrings.GetObject(Index: Integer): TObject;
begin
Result := nil;
end;
function TStrings.GetText: PChar;
begin
Result := StrNew(PChar(GetTextStr));
end;
function TStrings.GetTextStr: string;
var
I, L, Size, Count: Integer;
P: PChar;
S: string;
begin
Count := GetCount;
Size := 0;
for I := 0 to Count - 1 do Inc(Size, Length(Get(I)) + 2);
SetString(Result, nil, Size);
P := Pointer(Result);
for I := 0 to Count - 1 do
begin
S := Get(I);
L := Length(S);
if L <> 0 then
begin
System.Move(Pointer(S)^, P^, L);
Inc(P, L);
end;
P^ := #13;
Inc(P);
P^ := #10;
Inc(P);
end;
end;
function TStrings.GetValue(const Name: string): string;
var
I: Integer;
begin
I := IndexOfName(Name);
if I >= 0 then
Result := Copy(Get(I), Length(Name) + 2, MaxInt) else
Result := '';
end;
function TStrings.IndexOf(const S: string): Integer;
begin
for Result := 0 to GetCount - 1 do
if AnsiCompareText(Get(Result), S) = 0 then Exit;
Result := -1;
end;
function TStrings.IndexOfName(const Name: string): Integer;
var
P: Integer;
S: string;
begin
for Result := 0 to GetCount - 1 do
begin
S := Get(Result);
P := Pos('=', S);
if (P <> 0) and (AnsiCompareText(Copy(S, 1, P - 1), Name) = 0) then Exit;
end;
Result := -1;
end;
function TStrings.IndexOfObject(AObject: TObject): Integer;
begin
for Result := 0 to GetCount - 1 do
if GetObject(Result) = AObject then Exit;
Result := -1;
end;
procedure TStrings.InsertObject(Index: Integer; const S: string;
AObject: TObject);
begin
Insert(Index, S);
PutObject(Index, AObject);
end;
procedure TStrings.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TStrings.LoadFromStream(Stream: TStream);
var
Size: Integer;
S: string;
begin
BeginUpdate;
try
Size := Stream.Size - Stream.Position;
SetString(S, nil, Size);
Stream.Read(Pointer(S)^, Size);
SetTextStr(S);
finally
EndUpdate;
end;
end;
procedure TStrings.Move(CurIndex, NewIndex: Integer);
var
TempObject: TObject;
TempString: string;
begin
if CurIndex <> NewIndex then
begin
TempString := Get(CurIndex);
TempObject := GetObject(CurIndex);
Delete(CurIndex);
InsertObject(NewIndex, TempString, TempObject);
end;
end;
procedure TStrings.Put(Index: Integer; const S: string);
var
TempObject: TObject;
begin
TempObject := GetObject(Index);
Delete(Index);
InsertObject(Index, S, TempObject);
end;
procedure TStrings.PutObject(Index: Integer; AObject: TObject);
begin
end;
procedure TStrings.ReadData(Reader: TReader);
begin
Reader.ReadListBegin;
Clear;
while not Reader.EndOfList do Add(Reader.ReadString);
Reader.ReadListEnd;
end;
procedure TStrings.SaveToFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
procedure TStrings.SaveToStream(Stream: TStream);
var
S: string;
begin
S := GetText;
Stream.WriteBuffer(Pointer(S)^, Length(S));
end;
procedure TStrings.SetCommaText(const Value: string);
var
P, P1, P2: PChar;
S: string;
Text: array[0..4095] of Char;
begin
BeginUpdate;
try
Clear;
StrLCopy(Text, PChar(Value), SizeOf(Text) - 1);
P := Text;
while (P^ <> #0) and (P^ <= ' ') do Inc(P);
if P^ <> #0 then
while True do
begin
P1 := P;
if P^ = '"' then
begin
P2 := P;
Inc(P);
while P^ <> #0 do
begin
if P^ = '"' then
begin
Inc(P);
if P^ <> '"' then Break;
end;
P2^ := P^;
Inc(P2);
Inc(P);
end;
end else
begin
while (P^ > ' ') and (P^ <> ',') do Inc(P);
P2 := P;
end;
SetString(S, P1, P2 - P1);
Add(S);
while (P^ <> #0) and (P^ <= ' ') do Inc(P);
if P^ = #0 then Break;
if P^ = ',' then
begin
Inc(P);
while (P^ <> #0) and (P^ <= ' ') do Inc(P);
end;
end;
finally
EndUpdate;
end;
end;
procedure TStrings.SetValue(const Name, Value: string);
var
I: Integer;
begin
I := IndexOfName(Name);
if Value <> '' then
begin
if I < 0 then I := Add('');
Put(I, Name + '=' + Value);
end else
begin
if I >= 0 then Delete(I);
end;
end;
procedure TStrings.SetText(Text: PChar);
begin
SetTextStr(Text);
end;
procedure TStrings.SetTextStr(const Value: string);
var
P, Start: PChar;
S: string;
begin
BeginUpdate;
try
Clear;
P := Pointer(Value);
if P <> nil then
while P^ <> #0 do
begin
Start := P;
while not (P^ in [#0, #10, #13]) do Inc(P);
SetString(S, Start, P - Start);
Add(S);
if P^ = #13 then Inc(P);
if P^ = #10 then Inc(P);
end;
finally
EndUpdate;
end;
end;
procedure TStrings.SetUpdateState(Updating: Boolean);
begin
end;
procedure TStrings.WriteData(Writer: TWriter);
var
I: Integer;
begin
Writer.WriteListBegin;
for I := 0 to Count - 1 do Writer.WriteString(Get(I));
Writer.WriteListEnd;
end;
{ TStringList }
destructor TStringList.Destroy;
begin
FOnChange := nil;
FOnChanging := nil;
if FCount <> 0 then Finalize(FList^[0], FCount);
FCount := 0;
SetCapacity(0);
end;
function TStringList.Add(const S: string): Integer;
begin
if not Sorted then
Result := FCount
else
if Find(S, Result) then
case Duplicates of
dupIgnore: Exit;
dupError: ListError(SDuplicateString);
end;
InsertItem(Result, S);
end;
procedure TStringList.Changed;
begin
if (FUpdateCount = 0) and Assigned(FOnChange) then FOnChange(Self);
end;
procedure TStringList.Changing;
begin
if (FUpdateCount = 0) and Assigned(FOnChanging) then FOnChanging(Self);
end;
procedure TStringList.Clear;
begin
if FCount <> 0 then
begin
Changing;
Finalize(FList^[0], FCount);
FCount := 0;
SetCapacity(0);
Changed;
end;
end;
procedure TStringList.Delete(Index: Integer);
begin
if (Index < 0) or (Index >= FCount) then ListIndexError;
Changing;
Finalize(FList^[Index]);
Dec(FCount);
if Index < FCount then
System.Move(FList^[Index + 1], FList^[Index],
(FCount - Index) * SizeOf(TStringItem));
Changed;
end;
procedure TStringList.Exchange(Index1, Index2: Integer);
begin
if (Index1 < 0) or (Index1 >= FCount) or
(Index2 < 0) or (Index2 >= FCount) then ListIndexError;
Changing;
ExchangeItems(Index1, Index2);
Changed;
end;
procedure TStringList.ExchangeItems(Index1, Index2: Integer);
var
Temp: Integer;
Item1, Item2: PStringItem;
begin
Item1 := @FList^[Index1];
Item2 := @FList^[Index2];
Temp := Integer(Item1^.FString);
Integer(Item1^.FString) := Integer(Item2^.FString);
Integer(Item2^.FString) := Temp;
Temp := Integer(Item1^.FObject);
Integer(Item1^.FObject) := Integer(Item2^.FObject);
Integer(Item2^.FObject) := Temp;
end;
function TStringList.Find(const S: string; var Index: Integer): Boolean;
var
L, H, I, C: Integer;
begin
Result := False;
L := 0;
H := FCount - 1;
while L <= H do
begin
I := (L + H) shr 1;
C := AnsiCompareText(FList^[I].FString, S);
if C < 0 then L := I + 1 else
begin
H := I - 1;
if C = 0 then
begin
Result := True;
if Duplicates <> dupAccept then L := I;
end;
end;
end;
Index := L;
end;
function TStringList.Get(Index: Integer): string;
begin
if (Index < 0) or (Index >= FCount) then ListIndexError;
Result := FList^[Index].FString;
end;
function TStringList.GetCount: Integer;
begin
Result := FCount;
end;
function TStringList.GetObject(Index: Integer): TObject;
begin
if (Index < 0) or (Index >= FCount) then ListIndexError;
Result := FList^[Index].FObject;
end;
procedure TStringList.Grow;
var
Delta: Integer;
begin
if FCapacity > 8 then Delta := 16 else
if FCapacity > 4 then Delta := 8 else
Delta := 4;
SetCapacity(FCapacity + Delta);
end;
function TStringList.IndexOf(const S: string): Integer;
begin
if not Sorted then Result := inherited IndexOf(S) else
if not Find(S, Result) then Result := -1;
end;
procedure TStringList.Insert(Index: Integer; const S: string);
begin
if Sorted then ListError(SSortedListError);
if (Index < 0) or (Index > FCount) then ListIndexError;
InsertItem(Index, S);
end;
procedure TStringList.InsertItem(Index: Integer; const S: string);
begin
Changing;
if FCount = FCapacity then Grow;
if Index < FCount then
System.Move(FList^[Index], FList^[Index + 1],
(FCount - Index) * SizeOf(TStringItem));
with FList^[Index] do
begin
Pointer(FString) := nil;
FObject := nil;
FString := S;
end;
Inc(FCount);
Changed;
end;
procedure TStringList.Put(Index: Integer; const S: string);
begin
if Sorted then ListError(SSortedListError);
if (Index < 0) or (Index >= FCount) then ListIndexError;
Changing;
FList^[Index].FString := S;
Changed;
end;
procedure TStringList.PutObject(Index: Integer; AObject: TObject);
begin
if (Index < 0) or (Index >= FCount) then ListIndexError;
Changing;
FList^[Index].FObject := AObject;
Changed;
end;
procedure TStringList.QuickSort(L, R: Integer);
var
I, J: Integer;
P: string;
begin
repeat
I := L;
J := R;
P := FList^[(L + R) shr 1].FString;
repeat
while AnsiCompareText(FList^[I].FString, P) < 0 do Inc(I);
while AnsiCompareText(FList^[J].FString, P) > 0 do Dec(J);
if I <= J then
begin
ExchangeItems(I, J);
Inc(I);
Dec(J);
end;
until I > J;
if L < J then QuickSort(L, J);
L := I;
until I >= R;
end;
procedure TStringList.SetCapacity(NewCapacity: Integer);
begin
ReallocMem(FList, NewCapacity * SizeOf(TStringItem));
FCapacity := NewCapacity;
end;
procedure TStringList.SetSorted(Value: Boolean);
begin
if FSorted <> Value then
begin
if Value then Sort;
FSorted := Value;
end;
end;
procedure TStringList.SetUpdateState(Updating: Boolean);
begin
if Updating then Changing else Changed;
end;
procedure TStringList.Sort;
begin
if not Sorted and (FCount > 1) then
begin
Changing;
QuickSort(0, FCount - 1);
Changed;
end;
end;
{ TStream }
function TStream.GetPosition: Longint;
begin
Result := Seek(0, 1);
end;
procedure TStream.SetPosition(Pos: Longint);
begin
Seek(Pos, 0);
end;
function TStream.GetSize: Longint;
var
Pos: Longint;
begin
Pos := Seek(0, 1);
Result := Seek(0, 2);
Seek(Pos, 0);
end;
procedure TStream.ReadBuffer(var Buffer; Count: Longint);
begin
if (Count <> 0) and (Read(Buffer, Count) <> Count) then
raise EReadError.CreateRes(SReadError);
end;
procedure TStream.WriteBuffer(const Buffer; Count: Longint);
begin
if (Count <> 0) and (Write(Buffer, Count) <> Count) then
raise EWriteError.CreateRes(SWriteError);
end;
function TStream.CopyFrom(Source: TStream; Count: Longint): Longint;
const
MaxBufSize = $F000;
var
BufSize, N: Integer;
Buffer: PChar;
begin
if Count = 0 then
begin
Source.Position := 0;
Count := Source.Size;
end;
Result := Count;
if Count > MaxBufSize then BufSize := MaxBufSize else BufSize := Count;
GetMem(Buffer, BufSize);
try
while Count <> 0 do
begin
if Count > BufSize then N := BufSize else N := Count;
Source.ReadBuffer(Buffer^, N);
WriteBuffer(Buffer^, N);
Dec(Count, N);
end;
finally
FreeMem(Buffer, BufSize);
end;
end;
function TStream.ReadComponent(Instance: TComponent): TComponent;
var
Reader: TReader;
begin
Reader := TReader.Create(Self, 4096);
try
Result := Reader.ReadRootComponent(Instance);
finally
Reader.Free;
end;
end;
procedure TStream.WriteComponent(Instance: TComponent);
begin
WriteDescendent(Instance, nil);
end;
procedure TStream.WriteDescendent(Instance, Ancestor: TComponent);
var
Writer: TWriter;
begin
Writer := TWriter.Create(Self, 4096);
try
Writer.WriteDescendent(Instance, Ancestor);
finally
Writer.Free;
end;
end;
function TStream.ReadComponentRes(Instance: TComponent): TComponent;
begin
ReadResHeader;
Result := ReadComponent(Instance);
end;
procedure TStream.WriteComponentRes(const ResName: string; Instance: TComponent);
begin
WriteDescendentRes(ResName, Instance, nil);
end;
procedure TStream.WriteDescendentRes(const ResName: string; Instance,
Ancestor: TComponent);
var
HeaderSize: Integer;
Origin, ImageSize: Longint;
Header: array[0..79] of Char;
begin
Byte((@Header[0])^) := $FF;
Word((@Header[1])^) := 10;
HeaderSize := StrLen(StrUpper(StrPLCopy(@Header[3], ResName, 63))) + 10;
Word((@Header[HeaderSize - 6])^) := $1030;
Longint((@Header[HeaderSize - 4])^) := 0;
WriteBuffer(Header, HeaderSize);
Origin := Position;
WriteDescendent(Instance, Ancestor);
ImageSize := Position - Origin;
Position := Origin - 4;
WriteBuffer(ImageSize, SizeOf(Longint));
Position := Origin + ImageSize;
end;
procedure TStream.ReadResHeader;
var
ReadCount: Longint;
Header: array[0..79] of Char;
begin
FillChar(Header, SizeOf(Header), 0);
ReadCount := Read(Header, SizeOf(Header) - 1);
if (Byte((@Header[0])^) = $FF) and (Word((@Header[1])^) = 10) then
Seek(StrLen(Header + 3) + 10 - ReadCount, 1)
else
raise EInvalidImage.CreateRes(SInvalidImage);
end;
{ THandleStream }
constructor THandleStream.Create(AHandle: Integer);
begin
FHandle := AHandle;
end;
function THandleStream.Read(var Buffer; Count: Longint): Longint;
begin
Result := FileRead(FHandle, Buffer, Count);
if Result = -1 then Result := 0;
end;
function THandleStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := FileWrite(FHandle, Buffer, Count);
if Result = -1 then Result := 0;
end;
function THandleStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
Result := FileSeek(FHandle, Offset, Origin);
end;
{ TFileStream }
constructor TFileStream.Create(const FileName: string; Mode: Word);
begin
if Mode = fmCreate then
begin
FHandle := FileCreate(FileName);
if FHandle < 0 then
raise EFCreateError.CreateResFmt(SFCreateError, [FileName]);
end else
begin
FHandle := FileOpen(FileName, Mode);
if FHandle < 0 then
raise EFOpenError.CreateResFmt(SFOpenError, [FileName]);
end;
end;
destructor TFileStream.Destroy;
begin
if FHandle >= 0 then FileClose(FHandle);
end;
{ TCustomMemoryStream }
procedure TCustomMemoryStream.SetPointer(Ptr: Pointer; Size: Longint);
begin
FMemory := Ptr;
FSize := Size;
end;
function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
Result := FSize - FPosition;
if Result > 0 then
begin
if Result > Count then Result := Count;
Move(Pointer(Longint(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
function TCustomMemoryStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
0: FPosition := Offset;
1: Inc(FPosition, Offset);
2: FPosition := FSize + Offset;
end;
Result := FPosition;
end;
procedure TCustomMemoryStream.SaveToStream(Stream: TStream);
begin
if FSize <> 0 then Stream.WriteBuffer(FMemory^, FSize);
end;
procedure TCustomMemoryStream.SaveToFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
{ TMemoryStream }
const
MemoryDelta = $2000; { Must be a power of 2 }
destructor TMemoryStream.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TMemoryStream.Clear;
begin
SetCapacity(0);
FSize := 0;
FPosition := 0;
end;
procedure TMemoryStream.LoadFromStream(Stream: TStream);
var
Count: Longint;
begin
Stream.Position := 0;
Count := Stream.Size;
SetSize(Count);
if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);
end;
procedure TMemoryStream.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TMemoryStream.SetCapacity(NewCapacity: Longint);
begin
SetPointer(Realloc(NewCapacity), FSize);
FCapacity := NewCapacity;
end;
procedure TMemoryStream.SetSize(NewSize: Longint);
begin
Clear;
SetCapacity(NewSize);
FSize := NewSize;
end;
function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer;
begin
if NewCapacity > 0 then
NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1);
Result := Memory;
if NewCapacity <> FCapacity then
begin
if NewCapacity = 0 then
begin
GlobalFreePtr(Memory);
Result := nil;
end else
begin
if Capacity = 0 then
Result := GlobalAllocPtr(HeapAllocFlags, NewCapacity)
else
Result := GlobalReallocPtr(Memory, NewCapacity, HeapAllocFlags);
if Result = nil then raise EStreamError.CreateRes(SMemoryStreamError);
end;
end;
end;
function TMemoryStream.Write(const Buffer; Count: Longint): Longint;
var
Pos: Longint;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
Pos := FPosition + Count;
if Pos > 0 then
begin
if Pos > FSize then
begin
if Pos > FCapacity then
SetCapacity(Pos);
FSize := Pos;
end;
System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count);
FPosition := Pos;
Result := Count;
Exit;
end;
end;
Result := 0;
end;
{ TResourceStream }
constructor TResourceStream.Create(Instance: THandle; const ResName: string;
ResType: PChar);
begin
inherited Create;
Initialize(Instance, PChar(ResName), ResType);
end;
constructor TResourceStream.CreateFromID(Instance: THandle; ResID: Integer;
ResType: PChar);
begin
inherited Create;
Initialize(Instance, PChar(ResID), ResType);
end;
procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar);
procedure Error;
begin
raise EResNotFound.Create(FmtLoadStr(SResNotFound, [Name]));
end;
begin
HResInfo := FindResource(Instance, Name, ResType);
if HResInfo = 0 then Error;
HGlobal := LoadResource(Instance, HResInfo);
if HGlobal = 0 then Error;
SetPointer(LockResource(HGlobal), SizeOfResource(Instance, HResInfo));
end;
destructor TResourceStream.Destroy;
begin
UnlockResource(HGlobal);
FreeResource(HResInfo);
inherited Destroy;
end;
function TResourceStream.Write(const Buffer; Count: Longint): Longint;
begin
raise EStreamError.CreateRes(SCantWriteResourceStreamError);
end;
{ TFiler }
constructor TFiler.Create(Stream: TStream; BufSize: Integer);
begin
FStream := Stream;
GetMem(FBuffer, BufSize);
FBufSize := BufSize;
end;
destructor TFiler.Destroy;
begin
if FBuffer <> nil then FreeMem(FBuffer, FBufSize);
end;
{ TPropFixup }
type
TPropFixup = class
FInstance: TPersistent;
FInstanceRoot: TComponent;
FPropInfo: PPropInfo;
FRootName: string;
FName: string;
constructor Create(Instance: TPersistent; InstanceRoot: TComponent;
PropInfo: PPropInfo; const RootName, Name: string);
end;
var
GlobalFixupList: TList;
constructor TPropFixup.Create(Instance: TPersistent; InstanceRoot: TComponent;
PropInfo: PPropInfo; const RootName, Name: string);
begin
FInstance := Instance;
FInstanceRoot := InstanceRoot;
FPropInfo := PropInfo;
FRootName := RootName;
FName := Name;
end;
procedure GlobalFixupReferences;
var
FinishedList: TList;
NotFinishedList: TList;
I: Integer;
Root: TComponent;
Instance: TPersistent;
procedure AddFinished(Instance: TPersistent);
begin
if (FinishedList.IndexOf(Instance) < 0) and
(NotFinishedList.IndexOf(Instance) >= 0) then
FinishedList.Add(Instance);
end;
procedure AddNotFinished(Instance: TPersistent);
var
Index: Integer;
begin
Index := FinishedList.IndexOf(Instance);
if Index <> -1 then FinishedList.Delete(Index);
if NotFinishedList.IndexOf(Instance) < 0 then
NotFinishedList.Add(Instance);
end;
begin
if Assigned(FindGlobalComponent) and (GlobalFixupList.Count > 0) then
begin
FinishedList := TList.Create;
try
NotFinishedList := TList.Create;
try
I := 0;
while I < GlobalFixupList.Count do
with TPropFixup(GlobalFixupList[I]) do
begin
Root := FindGlobalComponent(FRootName);
if (Root <> nil) or (GetOrdProp(FInstance, FPropInfo) <> 0) then
begin
if Root <> nil then
SetOrdProp(FInstance, FPropInfo,
Longint(Root.FindComponent(FName)));
AddFinished(FInstance);
GlobalFixupList.Delete(I);
Free;
end else
begin
AddNotFinished(FInstance);
Inc(I);
end;
end;
finally
NotFinishedList.Free;
end;
for I := 0 to FinishedList.Count - 1 do
begin
Instance := FinishedList[I];
if Instance is TComponent then
Exclude(TComponent(Instance).FComponentState, csFixups);
end;
finally
FinishedList.Free;
end;
end;
end;
function NameInStrings(Strings: TStrings; const Name: string): Boolean;
var
I: Integer;
begin
Result := True;
for I := 0 to Strings.Count - 1 do
if CompareText(Name, Strings[I]) = 0 then Exit;
Result := False;
end;
procedure GetFixupReferenceNames(Root: TComponent; Names: TStrings);
var
I: Integer;
Fixup: TPropFixup;
begin
for I := 0 to GlobalFixupList.Count - 1 do
begin
Fixup := GlobalFixupList[I];
if ((Root = nil) or (Fixup.FInstanceRoot = Root)) and
not NameInStrings(Names, Fixup.FRootName) then
Names.Add(Fixup.FRootName);
end;
end;
procedure RedirectFixupReferences(Root: TComponent; const OldRootName,
NewRootName: string);
var
I: Integer;
Fixup: TPropFixup;
begin
for I := 0 to GlobalFixupList.Count - 1 do
begin
Fixup := GlobalFixupList[I];
if ((Root = nil) or (Fixup.FInstanceRoot = Root)) and
(CompareText(OldRootName, Fixup.FRootName) = 0) then
Fixup.FRootName := NewRootName;
end;
GlobalFixupReferences;
end;
procedure RemoveFixupReferences(Root: TComponent; const RootName: string);
var
I: Integer;
Fixup: TPropFixup;
begin
for I := GlobalFixupList.Count - 1 downto 0 do
begin
Fixup := GlobalFixupList[I];
if ((Root = nil) or (Fixup.FInstanceRoot = Root)) and
((RootName = '') or (CompareText(RootName, Fixup.FRootName) = 0)) then
begin
GlobalFixupList.Delete(I);
Fixup.Free;
end;
end;
end;
procedure GetFixupInstanceNames(Root: TComponent;
const ReferenceRootName: string; Names: TStrings);
var
I: Integer;
Fixup: TPropFixup;
begin
for I := 0 to GlobalFixupList.Count - 1 do
begin
Fixup := GlobalFixupList[I];
if (Fixup.FInstanceRoot = Root) and
(CompareText(ReferenceRootName, Fixup.FRootName) = 0) and
not NameInStrings(Names, Fixup.FName) then
Names.Add(Fixup.FName);
end;
end;
{ TReader }
procedure ReadError(Ident: Integer);
begin
raise EReadError.CreateRes(Ident);
end;
procedure PropValueError;
begin
ReadError(SInvalidPropertyValue);
end;
procedure PropertyNotFound;
begin
ReadError(SUnknownProperty);
end;
function EnumValue(EnumType: PTypeInfo; const EnumName: string): Integer;
begin
Result := GetEnumValue(EnumType, EnumName);
if Result = -1 then PropValueError;
end;
destructor TReader.Destroy;
begin
FStream.Seek(Integer(FBufPos) - Integer(FBufEnd), 1);
inherited Destroy;
end;
procedure TReader.BeginReferences;
begin
FLoaded := TList.Create;
try
FFixups := TList.Create;
except
FLoaded.Free;
raise;
end;
end;
procedure TReader.CheckValue(Value: TValueType);
begin
if ReadValue <> Value then
begin
Dec(FBufPos);
SkipValue;
PropValueError;
end;
end;
procedure TReader.DefineProperty(const Name: string;
ReadData: TReaderProc; WriteData: TWriterProc; HasData: Boolean);
begin
if CompareText(Name, FPropName) = 0 then
begin
ReadData(Self);
FPropName := '';
end;
end;
procedure TReader.DefineBinaryProperty(const Name: string;
ReadData, WriteData: TStreamProc; HasData: Boolean);
var
Stream: TMemoryStream;
Count: Longint;
begin
if CompareText(Name, FPropName) = 0 then
begin
if ReadValue <> vaBinary then
begin
Dec(FBufPos);
SkipValue;
FCanHandleExcepts := True;
PropValueError;
end;
Stream := TMemoryStream.Create;
try
Read(Count, SizeOf(Count));
Stream.SetSize(Count);
Read(Stream.Memory^, Count);
FCanHandleExcepts := True;
ReadData(Stream);
finally
Stream.Free;
end;
FPropName := '';
end;
end;
function TReader.EndOfList: Boolean;
begin
Result := ReadValue = vaNull;
Dec(FBufPos);
end;
procedure TReader.EndReferences;
begin
FreeFixups;
FLoaded.Free;
FLoaded := nil;
end;
function TReader.Error(const Message: string): Boolean;
begin
Result := False;
if Assigned(FOnError) then FOnError(Self, Message, Result);
end;
function TReader.FindMethod(Root: TComponent;
const MethodName: string): Pointer;
var
Error: Boolean;
begin
Result := Root.MethodAddress(MethodName);
Error := Result = nil;
if Assigned(FOnFindMethod) then FOnFindMethod(Self, MethodName, Result, Error);
if Error then PropValueError;
end;
procedure TReader.DoFixupReferences;
var
I: Integer;
begin
if FFixups <> nil then
try
for I := 0 to FFixups.Count - 1 do
with TPropFixup(FFixups[I]) do
SetOrdProp(FInstance, FPropInfo,
Longint(FRoot.FindComponent(FName)));
finally
FreeFixups;
end;
end;
procedure TReader.FixupReferences;
var
I: Integer;
begin
DoFixupReferences;
GlobalFixupReferences;
for I := 0 to FLoaded.Count - 1 do TComponent(FLoaded[I]).Loaded;
end;
procedure TReader.FlushBuffer;
begin
FStream.Position := FStream.Position - (FBufEnd - FBufPos);
FBufPos := 0;
FBufEnd := 0;
end;
procedure TReader.FreeFixups;
var
I: Integer;
begin
if FFixups <> nil then
begin
for I := 0 to FFixups.Count - 1 do TPropFixup(FFixups[I]).Free;
FFixups.Free;
FFixups := nil;
end;
end;
function TReader.GetPosition: Longint;
begin
Result := FStream.Position + FBufPos;
end;
function TReader.NextValue: TValueType;
begin
Result := ReadValue;
Dec(FBufPos);
end;
procedure TReader.PropertyError;
begin
SkipValue;
PropertyNotFound;
end;
procedure TReader.Read(var Buf; Count: Longint); assembler;
asm
PUSH ESI
PUSH EDI
PUSH EBX
MOV EDI,EDX
MOV EBX,ECX
MOV ESI,EAX
JMP @@6
@@1: MOV ECX,[ESI].TReader.FBufEnd
SUB ECX,[ESI].TReader.FBufPos
JA @@2
MOV EAX,ESI
CALL TReader.ReadBuffer
MOV ECX,[ESI].TReader.FBufEnd
@@2: CMP ECX,EBX
JB @@3
MOV ECX,EBX
@@3: PUSH ESI
SUB EBX,ECX
MOV EAX,[ESI].TReader.FBuffer
ADD EAX,[ESI].TReader.FBufPos
ADD [ESI].TReader.FBufPos,ECX
MOV ESI,EAX
MOV EDX,ECX
SHR ECX,2
CLD
REP MOVSD
MOV ECX,EDX
AND ECX,3
REP MOVSB
POP ESI
@@6: OR EBX,EBX
JNE @@1
POP EBX
POP EDI
POP ESI
end;
procedure TReader.ReadBuffer;
begin
FBufEnd := FStream.Read(FBuffer^, FBufSize);
if FBufEnd = 0 then raise EReadError.CreateRes(SReadError);
FBufPos := 0;
end;
function TReader.ReadBoolean: Boolean;
begin
Result := ReadValue = vaTrue;
end;
function TReader.ReadChar: Char;
begin
CheckValue(vaString);
Read(Result, 1);
if Ord(Result) <> 1 then
begin
Dec(FBufPos);
ReadStr;
PropValueError;
end;
Read(Result, 1);
end;
procedure TReader.ReadCollection(Collection: TCollection);
var
Item: TPersistent;
Index: Integer;
begin
Index := 0;
Collection.BeginUpdate;
try
while not EndOfList do
begin
if NextValue in [vaInt8, vaInt16, vaInt32] then Index := ReadInteger;
while Collection.Count <= Index do Collection.Add;
Item := Collection.Items[Index];
ReadListBegin;
while not EndOfList do ReadProperty(Item);
ReadListEnd;
Inc(Index);
end;
ReadListEnd;
finally
Collection.EndUpdate;
end;
end;
function TReader.ReadComponent(Component: TComponent): TComponent;
var
CompClass, CompName: string;
Flags: TFilerFlags;
Position: Integer;
function ComponentCreated: Boolean;
begin
Result := not (ffInherited in Flags) and (Component = nil);
end;
function Recover(var Component: TComponent): Boolean;
begin
Result := False;
if not (ExceptObject is Exception) then Exit;
if ComponentCreated then Component.Free;
Component := nil;
SkipComponent(False);
Result := Error(Exception(ExceptObject).Message);
end;
procedure CreateComponent;
begin
try
Result := TComponentClass(FindFieldClass(Root, CompClass)).Create(Owner);
Include(Result.FComponentState, csLoading);
except
if not Recover(Result) then raise;
end;
end;
procedure SetCompName;
begin
try
Result.SetParentComponent(Parent);
SetName(Result, CompName);
except
if not Recover(Result) then raise;
end;
end;
procedure FindExistingComponent;
begin
try
Result := Root.FindComponent(CompName);
if Result = nil then
raise EReadError.CreateResFmt(SAncestorNotFound, [CompName]);
except
if not Recover(Result) then raise;
end;
end;
begin
ReadPrefix(Flags, Position);
CompClass := ReadStr;
CompName := ReadStr;
Result := Component;
if Result = nil then
if ffInherited in Flags then
FindExistingComponent else
CreateComponent;
if Result <> nil then
try
Include(Result.FComponentState, csLoading);
if not (ffInherited in Flags) then SetCompName;
if Result = nil then Exit;
Include(Result.FComponentState, csReading);
Result.ReadState(Self);
Exclude(Result.FComponentState, csReading);
if ffChildPos in Flags then Parent.SetChildOrder(Result, Position);
FLoaded.Add(Result);
except
if ComponentCreated then Result.Free;
raise;
end;
end;
procedure TReader.ReadData(Instance: TComponent);
begin
if FFixups = nil then
begin
FFixups := TList.Create;
try
ReadDataInner(Instance);
DoFixupReferences;
finally
FreeFixups;
end;
end else
ReadDataInner(Instance);
end;
procedure TReader.ReadDataInner(Instance: TComponent);
var
OldParent, OldOwner: TComponent;
begin
while not EndOfList do ReadProperty(Instance);
ReadListEnd;
OldParent := Parent;
OldOwner := Owner;
Parent := Instance.GetChildParent;
try
Owner := Instance.GetChildOwner;
if not Assigned(Owner) then Owner := Root;
while not EndOfList do ReadComponent(nil);
ReadListEnd;
finally
Parent := OldParent;
Owner := OldOwner;
end;
end;
function TReader.ReadFloat: Extended;
begin
if ReadValue = vaExtended then Read(Result, SizeOf(Result)) else
begin
Dec(FBufPos);
Result := ReadInteger;
end;
end;
function TReader.ReadIdent: string;
var
L: Byte;
begin
case ReadValue of
vaIdent:
begin
Read(L, SizeOf(Byte));
SetString(Result, PChar(nil), L);
Read(Result[1], L);
end;
vaFalse:
Result := 'False';
vaTrue:
Result := 'True';
vaNil:
Result := 'nil';
else
PropValueError;
end;
end;
function TReader.ReadInteger: Longint;
var
S: Shortint;
I: Smallint;
begin
case ReadValue of
vaInt8:
begin
Read(S, SizeOf(Shortint));
Result := S;
end;
vaInt16:
begin
Read(I, SizeOf(I));
Result := I;
end;
vaInt32:
Read(Result, SizeOf(Result));
else
PropValueError;
end;
end;
procedure TReader.ReadListBegin;
begin
CheckValue(vaList);
end;
procedure TReader.ReadListEnd;
begin
CheckValue(vaNull);
end;
procedure TReader.ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer);
var
Prefix: Byte;
begin
Flags := [];
if Byte(NextValue) and $F0 = $F0 then
begin
Prefix := Byte(ReadValue);
Byte(Flags) := Prefix and $0F;
if ffChildPos in Flags then AChildPos := ReadInteger;
end;
end;
procedure TReader.ReadProperty(AInstance: TPersistent);
var
I, J, L: Integer;
Instance: TPersistent;
PropInfo: PPropInfo;
PropValue: TObject;
PropPath: string;
procedure HandleException(E: Exception);
var
Name: string;
begin
Name := '';
if AInstance is TComponent then
Name := TComponent(AInstance).Name;
if Name = '' then Name := AInstance.ClassName;
raise EReadError.CreateResFmt(SPropertyException,
[Name, PropPath, E.Message]);
end;
procedure PropPathError;
begin
SkipValue;
ReadError(SInvalidPropertyPath);
end;
begin
try
PropPath := ReadStr;
try
I := 1;
L := Length(PropPath);
Instance := AInstance;
FCanHandleExcepts := True;
while True do
begin
J := I;
while (I <= L) and (PropPath[I] <> '.') do Inc(I);
FPropName := Copy(PropPath, J, I - J);
if I > L then Break;
PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
if PropInfo = nil then PropertyError;
PropValue := nil;
if PropInfo^.PropType^.Kind = tkClass then
PropValue := TObject(GetOrdProp(Instance, PropInfo));
if not (PropValue is TPersistent) then PropPathError;
Instance := TPersistent(PropValue);
Inc(I);
end;
PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
if PropInfo <> nil then ReadPropValue(Instance, PropInfo) else
begin
{ Cannot reliably recover from an error in a defined property }
FCanHandleExcepts := False;
Instance.DefineProperties(Self);
FCanHandleExcepts := True;
if FPropName <> '' then PropertyError;
end;
except
on E: Exception do HandleException(E);
end;
except
on E: Exception do
if not FCanHandleExcepts or not Error(E.Message) then raise;
end;
end;
procedure TReader.ReadPropValue(Instance: TPersistent; PropInfo: Pointer);
const
NilMethod: TMethod = (Code: nil; Data: nil);
var
PropType: PTypeInfo;
Method: TMethod;
procedure SetIntIdent(Instance: TPersistent; PropInfo: Pointer;
const Ident: string);
var
I: Integer;
V: Longint;
begin
for I := 0 to IntConstList.Count - 1 do
with TIntConst(IntConstList[I]) do
if PPropInfo(PropInfo)^.PropType = IntegerType then
if IdentToInt(Ident, V) then
begin
SetOrdProp(Instance, PropInfo, V);
Exit;
end;
PropValueError;
end;
procedure SetObjectIdent(Instance: TPersistent; PropInfo: Pointer;
const Ident: string);
var
RootName, Name: string;
P: Integer;
Fixup: TPropFixup;
begin
RootName := '';
Name := Ident;
P := Pos('.', Ident);
if P <> 0 then
begin
RootName := Copy(Ident, 1, P - 1);
Name := Copy(Ident, P + 1, MaxInt);
end;
Fixup := TPropFixup.Create(Instance, Root, PropInfo, RootName, Name);
if RootName = '' then
FFixups.Add(Fixup) else
GlobalFixupList.Add(Fixup);
end;
begin
if PPropInfo(PropInfo)^.SetProc = nil then ReadError(SReadOnlyProperty);
PropType := PPropInfo(PropInfo)^.PropType;
case PropType^.Kind of
tkInteger:
if NextValue = vaIdent then
SetIntIdent(Instance, PropInfo, ReadIdent) else
SetOrdProp(Instance, PropInfo, ReadInteger);
tkChar:
SetOrdProp(Instance, PropInfo, Ord(ReadChar));
tkEnumeration:
SetOrdProp(Instance, PropInfo, EnumValue(PropType, ReadIdent));
tkFloat:
SetFloatProp(Instance, PropInfo, ReadFloat);
tkString, tkLString:
SetStrProp(Instance, PropInfo, ReadString);
tkSet:
SetOrdProp(Instance, PropInfo, ReadSet(PropType));
tkClass:
case NextValue of
vaNil:
begin
ReadValue;
SetOrdProp(Instance, PropInfo, 0)
end;
vaCollection:
begin
ReadValue;
ReadCollection(TCollection(GetOrdProp(Instance, PropInfo)));
end
else
SetObjectIdent(Instance, PropInfo, ReadIdent);
end;
tkMethod:
if NextValue = vaNil then
begin
ReadValue;
SetMethodProp(Instance, PropInfo, NilMethod);
end
else
begin
Method.Code := FindMethod(Root, ReadIdent);
Method.Data := Root;
if Method.Code <> nil then SetMethodProp(Instance, PropInfo, Method);
end;
end;
end;
function TReader.ReadRootComponent(Root: TComponent): TComponent;
function FindUniqueName(const Name: string): string;
var
I: Integer;
begin
I := 0;
Result := '';
if Assigned(FindGlobalComponent) then
begin
Result := Name;
while FindGlobalComponent(Result) <> nil do
begin
Inc(I);
Result := Format('%s_%d', [Name, I]);
end;
end;
end;
var
I: Integer;
Flags: TFilerFlags;
begin
ReadSignature;
Result := nil;
try
ReadPrefix(Flags, I);
if Root = nil then
begin
Result := TComponentClass(FindClass(ReadStr)).Create(nil);
Result.Name := ReadStr;
end else
begin
Result := Root;
ReadStr; { Ignore class name }
if csDesigning in Result.ComponentState then
ReadStr else
Result.Name := FindUniqueName(ReadStr);
end;
FRoot := Result;
if GlobalLoaded <> nil then
FLoaded := GlobalLoaded else
FLoaded := TList.Create;
try
FLoaded.Add(FRoot);
FOwner := FRoot;
Include(FRoot.FComponentState, csLoading);
Include(FRoot.FComponentState, csReading);
FRoot.ReadState(Self);
Exclude(FRoot.FComponentState, csReading);
if GlobalLoaded = nil then
for I := 0 to FLoaded.Count - 1 do TComponent(FLoaded[I]).Loaded;
finally
if GlobalLoaded = nil then FLoaded.Free;
FLoaded := nil;
end;
GlobalFixupReferences;
except
RemoveFixupReferences(Root, '');
if Root = nil then Result.Free;
raise;
end;
end;
procedure TReader.ReadComponents(AOwner, AParent: TComponent;
Proc: TReadComponentsProc);
var
Component: TComponent;
begin
Root := AOwner;
Owner := AOwner;
Parent := AParent;
BeginReferences;
try
while not EndOfList do
begin
ReadSignature;
Component := ReadComponent(nil);
Proc(Component);
end;
FixupReferences;
finally
EndReferences;
end;
end;
function TReader.ReadSet(SetType: Pointer): Integer;
var
EnumType: PTypeInfo;
EnumName: string;
begin
try
if ReadValue <> vaSet then PropValueError;
EnumType := GetTypeData(SetType)^.CompType;
Result := 0;
while True do
begin
EnumName := ReadStr;
if EnumName = '' then Break;
Include(TIntegerSet(Result), EnumValue(EnumType, EnumName));
end;
except
SkipSetBody;
raise;
end;
end;
procedure TReader.ReadSignature;
var
Signature: Longint;
begin
Read(Signature, SizeOf(Signature));
if Signature <> Longint(FilerSignature) then ReadError(SInvalidImage);
end;
function TReader.ReadStr: string;
var
L: Byte;
begin
Read(L, SizeOf(Byte));
SetString(Result, PChar(nil), L);
Read(Result[1], L);
end;
function TReader.ReadString: string;
var
L: Integer;
begin
L := 0;
case ReadValue of
vaString:
Read(L, SizeOf(Byte));
vaLString:
Read(L, SizeOf(Integer));
else
PropValueError;
end;
SetString(Result, PChar(nil), L);
Read(Pointer(Result)^, L);
end;
function TReader.ReadValue: TValueType;
begin
Read(Result, SizeOf(Result));
end;
procedure TReader.SetPosition(Value: Longint);
begin
FStream.Position := Value;
FBufPos := 0;
FBufEnd := 0;
end;
procedure TReader.SkipSetBody;
begin
while ReadStr <> '' do begin end;
end;
procedure TReader.SkipValue;
procedure SkipList;
begin
while not EndOfList do SkipValue;
ReadListEnd;
end;
procedure SkipBytes(Count: Longint);
var
Bytes: array[0..255] of Char;
begin
while Count > 0 do
if Count > SizeOf(Bytes) then
begin
Read(Bytes, SizeOf(Bytes));
Dec(Count, SizeOf(Bytes));
end
else
begin
Read(Bytes, Count);
Count := 0;
end;
end;
procedure SkipBinary;
var
Count: Longint;
begin
Read(Count, SizeOf(Count));
SkipBytes(Count);
end;
begin
case ReadValue of
vaNull: begin end;
vaList: SkipList;
vaInt8: SkipBytes(1);
vaInt16: SkipBytes(2);
vaInt32: SkipBytes(4);
vaExtended: SkipBytes(SizeOf(Extended));
vaString, vaIdent: ReadStr;
vaFalse, vaTrue: begin end;
vaBinary: SkipBinary;
vaSet: SkipSetBody;
end;
end;
procedure TReader.SkipProperty;
begin
ReadStr; { Skips property name }
SkipValue;
end;
procedure TReader.SkipComponent(SkipHeader: Boolean);
var
Flags: TFilerFlags;
Position: Integer;
begin
if SkipHeader then
begin
ReadPrefix(Flags, Position);
ReadStr;
ReadStr;
end;
while not EndOfList do SkipProperty;
ReadListEnd;
while not EndOfList do SkipComponent(True);
ReadListEnd;
end;
procedure TReader.SetName(Component: TComponent; var Name: string);
begin
if Assigned(FOnSetName) then FOnSetName(Self, Component, Name);
Component.Name := Name;
end;
{ TWriter }
destructor TWriter.Destroy;
begin
WriteBuffer;
inherited Destroy;
end;
procedure TWriter.AddAncestor(Component: TComponent);
begin
FAncestorList.Add(Component);
end;
procedure TWriter.DefineProperty(const Name: string;
ReadData: TReaderProc; WriteData: TWriterProc; HasData: Boolean);
begin
if HasData then
begin
WritePropName(Name);
WriteData(Self);
end;
end;
procedure TWriter.DefineBinaryProperty(const Name: string;
ReadData, WriteData: TStreamProc; HasData: Boolean);
begin
if HasData then
begin
WritePropName(Name);
WriteBinary(WriteData);
end;
end;
function TWriter.GetPosition: Longint;
begin
Result := FStream.Position + FBufPos;
end;
procedure TWriter.FlushBuffer;
begin
WriteBuffer;
end;
procedure TWriter.SetPosition(Value: Longint);
var
StreamPosition: Longint;
begin
StreamPosition := FStream.Position;
{ Only flush the buffer if the repostion is outside the buffer range }
if (Value < StreamPosition) or (Value > StreamPosition + FBufPos) then
begin
WriteBuffer;
FStream.Position := Value;
end
else FBufPos := Value - StreamPosition;
end;
procedure TWriter.Write(const Buf; Count: Longint); assembler;
asm
PUSH ESI
PUSH EDI
PUSH EBX
MOV ESI,EDX
MOV EBX,ECX
MOV EDI,EAX
JMP @@6
@@1: MOV ECX,[EDI].TWriter.FBufSize
SUB ECX,[EDI].TWriter.FBufPos
JA @@2
MOV EAX,EDI
CALL TWriter.WriteBuffer
MOV ECX,[EDI].TWriter.FBufSize
@@2: CMP ECX,EBX
JB @@3
MOV ECX,EBX
@@3: SUB EBX,ECX
PUSH EDI
MOV EAX,[EDI].TWriter.FBuffer
ADD EAX,[EDI].TWriter.FBufPos
ADD [EDI].TWriter.FBufPos,ECX
@@5: MOV EDI,EAX
MOV EDX,ECX
SHR ECX,2
CLD
REP MOVSD
MOV ECX,EDX
AND ECX,3
REP MOVSB
POP EDI
@@6: OR EBX,EBX
JNE @@1
POP EBX
POP EDI
POP ESI
end;
procedure TWriter.WriteBinary(WriteData: TStreamProc);
var
Stream: TMemoryStream;
Count: Longint;
begin
Stream := TMemoryStream.Create;
try
WriteData(Stream);
WriteValue(vaBinary);
Count := Stream.Size;
Write(Count, SizeOf(Count));
Write(Stream.Memory^, Count);
finally
Stream.Free;
end;
end;
procedure TWriter.WriteBuffer;
begin
FStream.WriteBuffer(FBuffer^, FBufPos);
FBufPos := 0;
end;
procedure TWriter.WriteBoolean(Value: Boolean);
begin
if Value then
WriteValue(vaTrue) else
WriteValue(vaFalse);
end;
procedure TWriter.WriteChar(Value: Char);
begin
WriteString(Value);
end;
procedure TWriter.WriteCollection(Value: TCollection);
var
I: Integer;
begin
WriteValue(vaCollection);
for I := 0 to Value.Count - 1 do
begin
WriteListBegin;
WriteProperties(Value.Items[I]);
WriteListEnd;
end;
WriteListEnd;
end;
procedure TWriter.WriteComponent(Component: TComponent);
function FindAncestor(const Name: string): TComponent;
var
I: Integer;
begin
for I := 0 to FAncestorList.Count - 1 do
begin
Result := FAncestorList[I];
if CompareText(Result.Name, Name) = 0 then Exit;
end;
Result := nil;
end;
begin
Include(Component.FComponentState, csWriting);
if Assigned(FAncestorList) then
Ancestor := FindAncestor(Component.Name);
Component.WriteState(Self);
Exclude(Component.FComponentState, csWriting);
end;
procedure TWriter.WriteData(Instance: TComponent);
var
PreviousPosition, PropertiesPosition: Longint;
OldAncestorList: TList;
OldAncestorPos, OldChildPos: Integer;
Flags: TFilerFlags;
begin
if FBufSize - FBufPos < Length(Instance.ClassName) +
Length(Instance.Name) + 1+5+3 then WriteBuffer;
{ Prefix + vaInt + integer + 2 end lists }
PreviousPosition := Position;
Flags := [];
if Ancestor <> nil then Include(Flags, ffInherited);
if (FAncestorList <> nil) and (FAncestorPos < FAncestorList.Count) and
((Ancestor = nil) or (FAncestorList[FAncestorPos] <> Ancestor)) then
Include(Flags, ffChildPos);
WritePrefix(Flags, FChildPos);
WriteStr(Instance.ClassName);
WriteStr(Instance.Name);
PropertiesPosition := Position;
if (FAncestorList <> nil) and (FAncestorPos < FAncestorList.Count) then
begin
if Ancestor <> nil then Inc(FAncestorPos);
Inc(FChildPos);
end;
WriteProperties(Instance);
WriteListEnd;
OldAncestorList := FAncestorList;
OldAncestorPos := FAncestorPos;
OldChildPos := FChildPos;
try
FAncestorList := nil;
FAncestorPos := 0;
FChildPos := 0;
if not IgnoreChildren then
try
if (FAncestor <> nil) and (FAncestor is TComponent) then
begin
FAncestorList := TList.Create;
TComponent(FAncestor).GetChildren(AddAncestor);
end;
Instance.GetChildren(WriteComponent);
finally
FAncestorList.Free;
end;
finally
FAncestorList := OldAncestorList;
FAncestorPos := OldAncestorPos;
FChildPos := OldChildPos;
end;
WriteListEnd;
if (Instance <> Root) and (Flags = [ffInherited]) and
(Position = PropertiesPosition + (1 + 1)) then { (1 + 1) is two end lists }
Position := PreviousPosition;
end;
procedure TWriter.WriteDescendent(Root: TComponent; AAncestor: TComponent);
begin
FRootAncestor := AAncestor;
FAncestor := AAncestor;
FRoot := Root;
WriteSignature;
WriteComponent(Root);
end;
procedure TWriter.WriteFloat(Value: Extended);
begin
WriteValue(vaExtended);
Write(Value, SizeOf(Extended));
end;
procedure TWriter.WriteIdent(const Ident: string);
begin
if CompareText(Ident, 'False') = 0 then WriteValue(vaFalse) else
if CompareText(Ident ,'True') = 0 then WriteValue(vaTrue) else
if CompareText(Ident, 'nil') = 0 then WriteValue(vaNil) else
begin
WriteValue(vaIdent);
WriteStr(Ident);
end;
end;
procedure TWriter.WriteInteger(Value: Longint);
begin
if (Value >= -128) and (Value <= 127) then
begin
WriteValue(vaInt8);
Write(Value, SizeOf(Shortint));
end else
if (Value >= -32768) and (Value <= 32767) then
begin
WriteValue(vaInt16);
Write(Value, SizeOf(Smallint));
end else
begin
WriteValue(vaInt32);
Write(Value, SizeOf(Longint));
end;
end;
procedure TWriter.WriteListBegin;
begin
WriteValue(vaList);
end;
procedure TWriter.WriteListEnd;
begin
WriteValue(vaNull);
end;
procedure TWriter.WritePrefix(Flags: TFilerFlags; AChildPos: Integer);
var
Prefix: Byte;
begin
if Flags <> [] then
begin
Prefix := $F0 or Byte(Flags);
Write(Prefix, SizeOf(Prefix));
if ffChildPos in Flags then WriteInteger(AChildPos);
end;
end;
procedure TWriter.WriteProperties(Instance: TPersistent);
var
I, Count: Integer;
PropInfo: PPropInfo;
PropList: PPropList;
begin
Count := GetTypeData(Instance.ClassInfo)^.PropCount;
if Count > 0 then
begin
GetMem(PropList, Count * SizeOf(Pointer));
try
GetPropInfos(Instance.ClassInfo, PropList);
for I := 0 to Count - 1 do
begin
PropInfo := PropList^[I];
if IsStoredProp(Instance, PropInfo) then
WriteProperty(Instance, PropInfo);
end;
finally
FreeMem(PropList, Count * SizeOf(Pointer));
end;
end;
Instance.DefineProperties(Self);
end;
procedure TWriter.WriteProperty(Instance: TPersistent; PropInfo: Pointer);
var
PropType: PTypeInfo;
function AncestorValid: Boolean;
begin
Result := (Ancestor <> nil) and ((Instance.ClassType = Ancestor.ClassType) or
(Instance = Root));
end;
procedure WritePropPath;
begin
WritePropName(PPropInfo(PropInfo)^.Name);
end;
procedure WriteSet(Value: Longint);
var
I: Integer;
BaseType: PTypeInfo;
begin
BaseType := GetTypeData(PropType)^.CompType;
WriteValue(vaSet);
for I := 0 to SizeOf(TIntegerSet) * 8 - 1 do
if I in TIntegerSet(Value) then WriteStr(GetEnumName(BaseType, I));
WriteStr('');
end;
procedure WriteIntProp(IntType: PTypeInfo; Value: Longint);
var
I: Integer;
Ident: string;
begin
for I := 0 to IntConstList.Count - 1 do
with TIntConst(IntConstList[I]) do
if IntType = IntegerType then
if IntToIdent(Value, Ident) then
begin
WriteIdent(Ident);
Exit;
end
else Break;
WriteInteger(Value);
end;
procedure WriteCollectionProp(Collection: TCollection);
var
SavePropPath: string;
begin
WritePropPath;
SavePropPath := FPropPath;
try
FPropPath := '';
WriteCollection(Collection);
finally
FPropPath := SavePropPath;
end;
end;
procedure WriteOrdProp;
var
Value: Longint;
function IsDefaultValue: Boolean;
begin
if AncestorValid then
Result := Value = GetOrdProp(Ancestor, PropInfo) else
Result := Value = PPropInfo(PropInfo)^.Default;
end;
begin
Value := GetOrdProp(Instance, PropInfo);
if not IsDefaultValue then
begin
WritePropPath;
case PropType^.Kind of
tkInteger:
WriteIntProp(PPropInfo(PropInfo)^.PropType, Value);
tkChar:
WriteChar(Chr(Value));
tkSet:
WriteSet(Value);
tkEnumeration:
WriteIdent(GetEnumName(PropType, Value));
end;
end;
end;
procedure WriteFloatProp;
var
Value: Extended;
function IsDefaultValue: Boolean;
begin
if AncestorValid then
Result := Value = GetFloatProp(Ancestor, PropInfo) else
Result := Value = 0;
end;
begin
Value := GetFloatProp(Instance, PropInfo);
if not IsDefaultValue then
begin
WritePropPath;
WriteFloat(Value);
end;
end;
procedure WriteStrProp;
var
Value: string;
function IsDefault: Boolean;
begin
if AncestorValid then
Result := Value = GetStrProp(Ancestor, PropInfo) else
Result := Value = '';
end;
begin
Value := GetStrProp(Instance, PropInfo);
if not IsDefault then
begin
WritePropPath;
WriteString(Value);
end;
end;
procedure WriteObjectProp;
var
Value: TObject;
OldAncestor: TPersistent;
SavePropPath, ComponentValue: string;
function IsDefault: Boolean;
var
AncestorValue: TObject;
begin
AncestorValue := nil;
if AncestorValid then
begin
AncestorValue := TObject(GetOrdProp(Ancestor, PropInfo));
if (AncestorValue <> nil) and (TComponent(AncestorValue).Owner = FRootAncestor) and
(Value <> nil) and (TComponent(Value).Owner = Root) and
(CompareText(TComponent(AncestorValue).Name, TComponent(Value).Name) = 0) then
AncestorValue := Value;
end;
Result := Value = AncestorValue;
end;
function GetComponentValue(Component: TComponent): string;
begin
if Component.Owner = Root then
Result := Component.Name
else if Component.Owner <> nil then
Result := Component.Owner.Name + '.' + Component.Name
else Result := '';
end;
begin
Value := TObject(GetOrdProp(Instance, PropInfo));
if (Value = nil) and not IsDefault then
begin
WritePropPath;
WriteValue(vaNil);
end
else if Value is TPersistent then
if Value is TComponent then
begin
if not IsDefault then
begin
ComponentValue := GetComponentValue(TComponent(Value));
if ComponentValue <> '' then
begin
WritePropPath;
WriteIdent(ComponentValue);
end
end
end else if Value is TCollection then
begin
if not AncestorValid or
not CollectionsEqual(TCollection(Value),
TCollection(GetOrdProp(Ancestor, PropInfo))) then
WriteCollectionProp(TCollection(Value));
end else
begin
OldAncestor := Ancestor;
SavePropPath := FPropPath;
FPropPath := FPropPath + PPropInfo(PropInfo)^.Name + '.';
if AncestorValid then
Ancestor := TPersistent(GetOrdProp(Ancestor, PropInfo));
WriteProperties(TPersistent(Value));
Ancestor := OldAncestor;
FPropPath := SavePropPath;
end
end;
procedure WriteMethodProp;
var
Value: TMethod;
function IsDefaultValue: Boolean;
var
DefaultCode: Pointer;
begin
DefaultCode := nil;
if AncestorValid then DefaultCode := GetMethodProp(Ancestor, PropInfo).Code;
Result := (Value.Code = DefaultCode) or
((Value.Code <> nil) and (Root.MethodName(Value.Code) = ''));
end;
begin
Value := GetMethodProp(Instance, PropInfo);
if not IsDefaultValue then
begin
WritePropPath;
if Value.Code = nil then
WriteValue(vaNil) else
WriteIdent(Root.MethodName(Value.Code));
end;
end;
begin
if PPropInfo(PropInfo)^.SetProc <> nil then
begin
PropType := PPropInfo(PropInfo)^.PropType;
case PropType^.Kind of
tkInteger, tkChar, tkEnumeration, tkSet: WriteOrdProp;
tkFloat: WriteFloatProp;
tkString, tkLString: WriteStrProp;
tkClass: WriteObjectProp;
tkMethod: WriteMethodProp;
end;
end;
end;
procedure TWriter.WritePropName(const PropName: string);
begin
WriteStr(FPropPath + PropName);
end;
procedure TWriter.WriteRootComponent(Root: TComponent);
begin
WriteDescendent(Root, nil);
end;
procedure TWriter.WriteSignature;
begin
Write(FilerSignature, SizeOf(FilerSignature));
end;
procedure TWriter.WriteStr(const Value: string);
var
L: Integer;
begin
L := Length(Value);
if L > 255 then L := 255;
Write(L, SizeOf(Byte));
Write(Value[1], L);
end;
procedure TWriter.WriteString(const Value: string);
var
L: Integer;
begin
L := Length(Value);
if L <= 255 then
begin
WriteValue(vaString);
Write(L, SizeOf(Byte));
end else
begin
WriteValue(vaLString);
Write(L, SizeOf(Integer));
end;
Write(Pointer(Value)^, L);
end;
procedure TWriter.WriteValue(Value: TValueType);
begin
Write(Value, SizeOf(Value));
end;
{ TParser }
const
ParseBufSize = 4096;
procedure BinToHex(Buffer, Text: PChar; BufSize: Integer); assembler;
asm
PUSH ESI
PUSH EDI
MOV ESI,EAX
MOV EDI,EDX
MOV EDX,0
JMP @@1
@@0: DB '0123456789ABCDEF'
@@1: LODSB
MOV DL,AL
AND DL,0FH
MOV AH,@@0.Byte[EDX]
MOV DL,AL
SHR DL,4
MOV AL,@@0.Byte[EDX]
STOSW
DEC ECX
JNE @@1
POP EDI
POP ESI
end;
function HexToBin(Text, Buffer: PChar; BufSize: Integer): Integer; assembler;
asm
PUSH ESI
PUSH EDI
PUSH EBX
MOV ESI,EAX
MOV EDI,EDX
MOV EBX,EDX
MOV EDX,0
JMP @@1
@@0: DB 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
DB -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
DB -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
DB -1,10,11,12,13,14,15
@@1: LODSW
CMP AL,'0'
JB @@2
CMP AL,'f'
JA @@2
MOV DL,AL
MOV AL,@@0.Byte[EDX-'0']
CMP AL,-1
JE @@2
SHL AL,4
CMP AH,'0'
JB @@2
CMP AH,'f'
JA @@2
MOV DL,AH
MOV AH,@@0.Byte[EDX-'0']
CMP AH,-1
JE @@2
OR AL,AH
STOSB
DEC ECX
JNE @@1
@@2: MOV EAX,EDI
SUB EAX,EBX
POP EBX
POP EDI
POP ESI
end;
constructor TParser.Create(Stream: TStream);
begin
FStream := Stream;
GetMem(FBuffer, ParseBufSize);
FBuffer[0] := #0;
FBufPtr := FBuffer;
FBufEnd := FBuffer + ParseBufSize;
FSourcePtr := FBuffer;
FSourceEnd := FBuffer;
FTokenPtr := FBuffer;
FSourceLine := 1;
NextToken;
end;
destructor TParser.Destroy;
begin
if FBuffer <> nil then
begin
FStream.Seek(Longint(FTokenPtr) - Longint(FBufPtr), 1);
FreeMem(FBuffer, ParseBufSize);
end;
end;
procedure TParser.CheckToken(T: Char);
begin
if Token <> T then
case T of
toSymbol:
Error(SIdentifierExpected);
toString:
Error(SStringExpected);
toInteger, toFloat:
Error(SNumberExpected);
else
ErrorFmt(SCharExpected, [T]);
end;
end;
procedure TParser.CheckTokenSymbol(const S: string);
begin
if not TokenSymbolIs(S) then ErrorFmt(SSymbolExpected, [S]);
end;
procedure TParser.Error(Ident: Integer);
begin
ErrorStr(LoadStr(Ident));
end;
procedure TParser.ErrorFmt(Ident: Integer; const Args: array of const);
begin
ErrorStr(FmtLoadStr(Ident, Args));
end;
procedure TParser.ErrorStr(const Message: string);
begin
raise EParserError.CreateResFmt(SParseError, [Message, FSourceLine]);
end;
procedure TParser.HexToBinary(Stream: TStream);
var
Count: Integer;
Buffer: array[0..255] of Char;
begin
SkipBlanks;
while FSourcePtr^ <> '}' do
begin
Count := HexToBin(FSourcePtr, Buffer, SizeOf(Buffer));
if Count = 0 then Error(SInvalidBinary);
Stream.Write(Buffer, Count);
Inc(FSourcePtr, Count * 2);
SkipBlanks;
end;
NextToken;
end;
function TParser.NextToken: Char;
var
I: Integer;
P, S: PChar;
begin
SkipBlanks;
P := FSourcePtr;
FTokenPtr := P;
case P^ of
'A'..'Z', 'a'..'z', '_':
begin
Inc(P);
while P^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do Inc(P);
Result := toSymbol;
end;
'#', '''':
begin
S := P;
while True do
case P^ of
'#':
begin
Inc(P);
I := 0;
while P^ in ['0'..'9'] do
begin
I := I * 10 + (Ord(P^) - Ord('0'));
Inc(P);
end;
S^ := Chr(I);
Inc(S);
end;
'''':
begin
Inc(P);
while True do
begin
case P^ of
#0, #10, #13:
Error(SInvalidString);
'''':
begin
Inc(P);
if P^ <> '''' then Break;
end;
end;
S^ := P^;
Inc(S);
Inc(P);
end;
end;
else
Break;
end;
FStringPtr := S;
Result := toString;
end;
'$':
begin
Inc(P);
while P^ in ['0'..'9', 'A'..'F', 'a'..'f'] do Inc(P);
Result := toInteger;
end;
'-', '0'..'9':
begin
Inc(P);
while P^ in ['0'..'9'] do Inc(P);
Result := toInteger;
while P^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
begin
Inc(P);
Result := toFloat;
end;
end;
else
Result := P^;
if Result <> toEOF then Inc(P);
end;
FSourcePtr := P;
FToken := Result;
end;
procedure TParser.ReadBuffer;
var
Count: Integer;
begin
Inc(FOrigin, FSourcePtr - FBuffer);
FSourceEnd[0] := FSaveChar;
Count := FBufPtr - FSourcePtr;
if Count <> 0 then Move(FSourcePtr[0], FBuffer[0], Count);
FBufPtr := FBuffer + Count;
Inc(FBufPtr, FStream.Read(FBufPtr[0], FBufEnd - FBufPtr));
FSourcePtr := FBuffer;
FSourceEnd := FBufPtr;
if FSourceEnd = FBufEnd then
begin
FSourceEnd := LineStart(FBuffer, FSourceEnd - 1);
if FSourceEnd = FBuffer then Error(SLineTooLong);
end;
FSaveChar := FSourceEnd[0];
FSourceEnd[0] := #0;
end;
procedure TParser.SkipBlanks;
begin
while True do
begin
case FSourcePtr^ of
#0:
begin
ReadBuffer;
if FSourcePtr^ = #0 then Exit;
Continue;
end;
#10:
Inc(FSourceLine);
#33..#255:
Exit;
end;
Inc(FSourcePtr);
end;
end;
function TParser.SourcePos: Longint;
begin
Result := FOrigin + (FTokenPtr - FBuffer);
end;
function TParser.TokenFloat: Extended;
begin
Result := StrToFloat(TokenString);
end;
function TParser.TokenInt: Longint;
begin
Result := StrToInt(TokenString);
end;
function TParser.TokenString: string;
var
L: Integer;
begin
if FToken = toString then
L := FStringPtr - FTokenPtr else
L := FSourcePtr - FTokenPtr;
SetString(Result, FTokenPtr, L);
end;
function TParser.TokenSymbolIs(const S: string): Boolean;
begin
Result := (Token = toSymbol) and (CompareText(S, TokenString) = 0);
end;
function TParser.TokenComponentIdent: String;
var
P: PChar;
begin
CheckToken(toSymbol);
P := FSourcePtr;
while P^ = '.' do
begin
Inc(P);
if not (P^ in ['A'..'Z', 'a'..'z', '_']) then
Error(SIdentifierExpected);
repeat
Inc(P)
until not (P^ in ['A'..'Z', 'a'..'z', '0'..'9', '_']);
end;
FSourcePtr := P;
Result := TokenString;
end;
{ Binary to text conversion }
procedure ObjectBinaryToText(Input, Output: TStream);
var
NestingLevel: Integer;
SaveSeparator: Char;
Reader: TReader;
Writer: TWriter;
procedure WriteIndent;
const
Blanks: array[0..1] of Char = ' ';
var
I: Integer;
begin
for I := 1 to NestingLevel do Writer.Write(Blanks, SizeOf(Blanks));
end;
procedure WriteStr(const S: string);
begin
Writer.Write(S[1], Length(S));
end;
procedure NewLine;
begin
WriteStr(#13#10);
WriteIndent;
end;
procedure ConvertValue; forward;
procedure ConvertHeader;
var
ClassName, ObjectName: string;
Flags: TFilerFlags;
Position: Integer;
begin
Reader.ReadPrefix(Flags, Position);
ClassName := Reader.ReadStr;
ObjectName := Reader.ReadStr;
WriteIndent;
if ffInherited in Flags then
WriteStr('inherited ')
else
WriteStr('object ');
if ObjectName <> '' then
begin
WriteStr(ObjectName);
WriteStr(': ');
end;
WriteStr(ClassName);
if ffChildPos in Flags then
begin
WriteStr(' [');
WriteStr(IntToStr(Position));
WriteStr(']');
end;
WriteStr(#13#10);
end;
procedure ConvertBinary;
const
BytesPerLine = 32;
var
MultiLine: Boolean;
I: Integer;
Count: Longint;
Buffer: array[0..BytesPerLine - 1] of Char;
Text: array[0..BytesPerLine * 2 - 1] of Char;
begin
Reader.ReadValue;
WriteStr('{');
Inc(NestingLevel);
Reader.Read(Count, SizeOf(Count));
MultiLine := Count >= BytesPerLine;
while Count > 0 do
begin
if MultiLine then NewLine;
if Count >= 32 then I := 32 else I := Count;
Reader.Read(Buffer, I);
BinToHex(Buffer, Text, I);
Writer.Write(Text, I * 2);
Dec(Count, I);
end;
Dec(NestingLevel);
WriteStr('}');
end;
procedure ConvertProperty; forward;
procedure ConvertValue;
var
I, J, L: Integer;
S: string;
begin
case Reader.NextValue of
vaList:
begin
Reader.ReadValue;
WriteStr('(');
Inc(NestingLevel);
while not Reader.EndOfList do
begin
NewLine;
ConvertValue;
end;
Reader.ReadListEnd;
Dec(NestingLevel);
WriteStr(')');
end;
vaInt8, vaInt16, vaInt32:
WriteStr(IntToStr(Reader.ReadInteger));
vaExtended:
WriteStr(FloatToStr(Reader.ReadFloat));
vaString, vaLString:
begin
S := Reader.ReadString;
L := Length(S);
if L = 0 then WriteStr('''''') else
begin
I := 1;
repeat
if (S[I] >= ' ') and (S[I] <> '''') then
begin
J := I;
repeat Inc(I) until (I > L) or (S[I] < ' ') or (S[I] = '''');
WriteStr('''');
Writer.Write(S[J], I - J);
WriteStr('''');
end else
begin
WriteStr('#');
WriteStr(IntToStr(Ord(S[I])));
Inc(I);
end;
until I > L;
end;
end;
vaIdent, vaFalse, vaTrue, vaNil:
WriteStr(Reader.ReadIdent);
vaBinary:
ConvertBinary;
vaSet:
begin
Reader.ReadValue;
WriteStr('[');
I := 0;
while True do
begin
S := Reader.ReadStr;
if S = '' then Break;
if I > 0 then WriteStr(', ');
WriteStr(S);
Inc(I);
end;
WriteStr(']');
end;
vaCollection:
begin
Reader.ReadValue;
WriteStr('<');
Inc(NestingLevel);
while not Reader.EndOfList do
begin
NewLine;
WriteStr('item');
if Reader.NextValue in [vaInt8, vaInt16, vaInt32] then
begin
WriteStr(' [');
ConvertValue;
WriteStr(']');
end;
WriteStr(#13#10);
Reader.CheckValue(vaList);
Inc(NestingLevel);
while not Reader.EndOfList do ConvertProperty;
Reader.ReadListEnd;
Dec(NestingLevel);
WriteIndent;
WriteStr('end');
end;
Reader.ReadListEnd;
Dec(NestingLevel);
WriteStr('>');
end;
end;
end;
procedure ConvertProperty;
begin
WriteIndent;
WriteStr(Reader.ReadStr);
WriteStr(' = ');
ConvertValue;
WriteStr(#13#10);
end;
procedure ConvertObject;
begin
ConvertHeader;
Inc(NestingLevel);
while not Reader.EndOfList do ConvertProperty;
Reader.ReadListEnd;
while not Reader.EndOfList do ConvertObject;
Reader.ReadListEnd;
Dec(NestingLevel);
WriteIndent;
WriteStr('end'#13#10);
end;
begin
NestingLevel := 0;
Reader := TReader.Create(Input, 4096);
SaveSeparator := DecimalSeparator;
DecimalSeparator := '.';
try
Writer := TWriter.Create(Output, 4096);
try
Reader.ReadSignature;
ConvertObject;
finally
Writer.Free;
end;
finally
DecimalSeparator := SaveSeparator;
Reader.Free;
end;
end;
{ Text to binary conversion }
procedure ObjectTextToBinary(Input, Output: TStream);
var
SaveSeparator: Char;
Parser: TParser;
Writer: TWriter;
function ConvertOrderModifier: Integer;
begin
Result := -1;
if Parser.Token = '[' then
begin
Parser.NextToken;
Parser.CheckToken(toInteger);
Result := Parser.TokenInt;
Parser.NextToken;
Parser.CheckToken(']');
Parser.NextToken;
end;
end;
procedure ConvertHeader(IsInherited: Boolean);
var
ClassName, ObjectName: string;
Flags: TFilerFlags;
Position: Integer;
begin
Parser.CheckToken(toSymbol);
ClassName := Parser.TokenString;
ObjectName := '';
if Parser.NextToken = ':' then
begin
Parser.NextToken;
Parser.CheckToken(toSymbol);
ObjectName := ClassName;
ClassName := Parser.TokenString;
Parser.NextToken;
end;
Flags := [];
Position := ConvertOrderModifier;
if IsInherited then
Include(Flags, ffInherited);
if Position > 0 then
Include(Flags, ffChildPos);
Writer.WritePrefix(Flags, Position);
Writer.WriteStr(ClassName);
Writer.WriteStr(ObjectName);
end;
procedure ConvertProperty; forward;
procedure ConvertValue;
var
Order: Integer;
begin
case Parser.Token of
toSymbol:
Writer.WriteIdent(Parser.TokenComponentIdent);
toString:
Writer.WriteString(Parser.TokenString);
toInteger:
Writer.WriteInteger(Parser.TokenInt);
toFloat:
Writer.WriteFloat(Parser.TokenFloat);
'[':
begin
Parser.NextToken;
Writer.WriteValue(vaSet);
if Parser.Token <> ']' then
while True do
begin
Parser.CheckToken(toSymbol);
Writer.WriteStr(Parser.TokenString);
if Parser.NextToken = ']' then Break;
Parser.CheckToken(',');
Parser.NextToken;
end;
Writer.WriteStr('');
end;
'(':
begin
Parser.NextToken;
Writer.WriteListBegin;
while Parser.Token <> ')' do ConvertValue;
Writer.WriteListEnd;
end;
'{':
Writer.WriteBinary(Parser.HexToBinary);
'<':
begin
Parser.NextToken;
Writer.WriteValue(vaCollection);
while Parser.Token <> '>' do
begin
Parser.CheckTokenSymbol('item');
Parser.NextToken;
Order := ConvertOrderModifier;
if Order <> -1 then Writer.WriteInteger(Order);
Writer.WriteListBegin;
while not Parser.TokenSymbolIs('end') do ConvertProperty;
Writer.WriteListEnd;
Parser.NextToken;
end;
Writer.WriteListEnd;
end;
else
Parser.Error(SInvalidProperty);
end;
Parser.NextToken;
end;
procedure ConvertProperty;
var
PropName: string;
begin
Parser.CheckToken(toSymbol);
PropName := Parser.TokenString;
Parser.NextToken;
while Parser.Token = '.' do
begin
Parser.NextToken;
Parser.CheckToken(toSymbol);
PropName := PropName + '.' + Parser.TokenString;
Parser.NextToken;
end;
Writer.WriteStr(PropName);
Parser.CheckToken('=');
Parser.NextToken;
ConvertValue;
end;
procedure ConvertObject;
var
InheritedObject: Boolean;
begin
InheritedObject := False;
if Parser.TokenSymbolIs('INHERITED') then
InheritedObject := True else
Parser.CheckTokenSymbol('OBJECT');
Parser.NextToken;
ConvertHeader(InheritedObject);
while not Parser.TokenSymbolIs('END') and
not Parser.TokenSymbolIs('OBJECT') and
not Parser.TokenSymbolIs('INHERITED') do ConvertProperty;
Writer.WriteListEnd;
while not Parser.TokenSymbolIs('END') do ConvertObject;
Writer.WriteListEnd;
Parser.NextToken;
end;
begin
Parser := TParser.Create(Input);
SaveSeparator := DecimalSeparator;
DecimalSeparator := '.';
try
Writer := TWriter.Create(Output, 4096);
try
Writer.WriteSignature;
ConvertObject;
finally
Writer.Free;
end;
finally
DecimalSeparator := SaveSeparator;
Parser.Free;
end;
end;
{ Resource to text conversion }
procedure ObjectResourceToText(Input, Output: TStream);
begin
Input.ReadResHeader;
ObjectBinaryToText(Input, Output);
end;
{ Text to resource conversion }
procedure ObjectTextToResource(Input, Output: TStream);
var
Len: Byte;
Tmp: Longint;
MemoryStream: TMemoryStream;
MemorySize: Longint;
Header: array[0..79] of Char;
begin
MemoryStream := TMemoryStream.Create;
try
ObjectTextToBinary(Input, MemoryStream);
MemorySize := MemoryStream.Size;
FillChar(Header, SizeOf(Header), 0);
MemoryStream.Position := SizeOf(Longint); { Skip header }
MemoryStream.Read(Len, 1);
{ Skip over object prefix if it is present }
if Len and $F0 = $F0 then
begin
if ffChildPos in TFilerFlags((Len and $F0)) then
begin
MemoryStream.Read(Len, 1);
case TValueType(Len) of
vaInt8: Len := 1;
vaInt16: Len := 2;
vaInt32: Len := 4;
end;
MemoryStream.Read(Tmp, Len);
end;
MemoryStream.Read(Len, 1);
end;
MemoryStream.Read(Header[3], Len);
StrUpper(@Header[3]);
Byte((@Header[0])^) := $FF;
Word((@Header[1])^) := 10;
Word((@Header[Len + 4])^) := $1030;
Longint((@Header[Len + 6])^) := MemorySize;
Output.Write(Header, Len + 10);
Output.Write(MemoryStream.Memory^, MemorySize);
finally
MemoryStream.Free;
end;
end;
{ Thread management routines }
const
CM_EXECPROC = $8FFF;
type
PRaiseFrame = ^TRaiseFrame;
TRaiseFrame = record
NextRaise: PRaiseFrame;
ExceptAddr: Pointer;
ExceptObject: TObject;
ExceptionRecord: PExceptionRecord;
end;
var
ThreadWindow: HWND;
ThreadCount: Integer;
function ThreadWndProc(Window: HWND; Message, wParam, lParam: Longint): Longint; stdcall;
begin
case Message of
CM_EXECPROC:
with TThread(lParam) do
begin
Result := 0;
try
FSynchronizeException := nil;
FMethod;
except
if RaiseList <> nil then
begin
FSynchronizeException := PRaiseFrame(RaiseList)^.ExceptObject;
PRaiseFrame(RaiseList)^.ExceptObject := nil;
end;
end;
end;
else
Result := DefWindowProc(Window, Message, wParam, lParam);
end;
end;
var
ThreadWindowClass: TWndClass = (
style: 0;
lpfnWndProc: @ThreadWndProc;
cbClsExtra: 0;
cbWndExtra: 0;
hInstance: 0;
hIcon: 0;
hCursor: 0;
hbrBackground: 0;
lpszMenuName: nil;
lpszClassName: 'TThreadWindow');
procedure AddThread;
function AllocateWindow: HWND;
var
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
ThreadWindowClass.hInstance := HInstance;
ClassRegistered := GetClassInfo(HInstance, ThreadWindowClass.lpszClassName,
TempClass);
if not ClassRegistered or (TempClass.lpfnWndProc <> @ThreadWndProc) then
begin
if ClassRegistered then
Windows.UnregisterClass(ThreadWindowClass.lpszClassName, HInstance);
Windows.RegisterClass(ThreadWindowClass);
end;
Result := CreateWindow(ThreadWindowClass.lpszClassName, '', 0,
0, 0, 0, 0, 0, 0, HInstance, nil);
end;
begin
if ThreadCount = 0 then
ThreadWindow := AllocateWindow;
Inc(ThreadCount);
end;
procedure RemoveThread;
begin
Dec(ThreadCount);
if ThreadCount = 0 then DestroyWindow(ThreadWindow);
end;
{ TThread }
function ThreadProc(Thread: TThread): Integer;
var
FreeThread: Boolean;
begin
Thread.Execute;
FreeThread := Thread.FFreeOnTerminate;
Result := Thread.FReturnValue;
Thread.FFinished := True;
Thread.DoTerminate;
if FreeThread then Thread.Free;
EndThread(Result);
end;
constructor TThread.Create(CreateSuspended: Boolean);
var
Flags: Integer;
begin
inherited Create;
AddThread;
FSuspended := CreateSuspended;
Flags := 0;
if CreateSuspended then Flags := CREATE_SUSPENDED;
FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), Flags, FThreadID);
end;
destructor TThread.Destroy;
begin
if not FFinished and not Suspended then
begin
Terminate;
WaitFor;
end;
if FHandle <> 0 then CloseHandle(FHandle);
inherited Destroy;
RemoveThread;
end;
procedure TThread.CallOnTerminate;
begin
FOnTerminate(Self);
end;
procedure TThread.DoTerminate;
begin
if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;
const
Priorities: array [TThreadPriority] of Integer =
(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL);
function TThread.GetPriority: TThreadPriority;
var
P: Integer;
I: TThreadPriority;
begin
P := GetThreadPriority(FHandle);
Result := tpNormal;
for I := Low(TThreadPriority) to High(TThreadPriority) do
if Priorities[I] = P then Result := I;
end;
procedure TThread.SetPriority(Value: TThreadPriority);
begin
SetThreadPriority(FHandle, Priorities[Value]);
end;
procedure TThread.Synchronize(Method: TThreadMethod);
begin
if FMainThreadWaiting then
raise EThread.CreateRes(SMainThreadWaiting);
FSynchronizeException := nil;
FMethod := Method;
SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self));
if Assigned(FSynchronizeException) then raise FSynchronizeException;
end;
procedure TThread.SetSuspended(Value: Boolean);
begin
if Value <> FSuspended then
if Value then
Suspend else
Resume;
end;
procedure TThread.Suspend;
begin
FSuspended := True;
SuspendThread(FHandle);
end;
procedure TThread.Resume;
begin
if ResumeThread(FHandle) = 1 then FSuspended := False;
end;
procedure TThread.Terminate;
begin
FTerminated := True;
end;
function TThread.WaitFor: Integer;
begin
if GetCurrentThreadID = MainThreadID then FMainThreadWaiting := True;
WaitForSingleObject(FHandle, INFINITE);
GetExitCodeThread(FHandle, Result);
end;
{ TComponent }
constructor TComponent.Create(AOwner: TComponent);
begin
FComponentStyle := [csInheritable];
if AOwner <> nil then AOwner.InsertComponent(Self);
end;
destructor TComponent.Destroy;
var
I: Integer;
begin
if FFreeNotifies <> nil then
begin
for I := 0 to FFreeNotifies.Count - 1 do
TComponent(FFreeNotifies[I]).Notification(Self, opRemove);
FFreeNotifies.Free;
FFreeNotifies := nil;
end;
Destroying;
DestroyComponents;
if FOwner <> nil then FOwner.RemoveComponent(Self);
end;
procedure TComponent.FreeNotification(AComponent: TComponent);
begin
if (Owner = nil) or (AComponent.Owner <> Owner) then
begin
if not Assigned(FFreeNotifies) then FFreeNotifies := TList.Create;
if FFreeNotifies.IndexOf(AComponent) < 0 then
begin
FFreeNotifies.Add(AComponent);
AComponent.FreeNotification(Self);
end;
end;
end;
procedure TComponent.ReadLeft(Reader: TReader);
begin
LongRec(FDesignInfo).Lo := Reader.ReadInteger;
end;
procedure TComponent.ReadTop(Reader: TReader);
begin
LongRec(FDesignInfo).Hi := Reader.ReadInteger;
end;
procedure TComponent.WriteLeft(Writer: TWriter);
begin
Writer.WriteInteger(LongRec(FDesignInfo).Lo);
end;
procedure TComponent.WriteTop(Writer: TWriter);
begin
Writer.WriteInteger(LongRec(FDesignInfo).Hi);
end;
procedure TComponent.Insert(AComponent: TComponent);
begin
if FComponents = nil then FComponents := TList.Create;
FComponents.Add(AComponent);
AComponent.FOwner := Self;
end;
procedure TComponent.Remove(AComponent: TComponent);
begin
AComponent.FOwner := nil;
FComponents.Remove(AComponent);
if FComponents.Count = 0 then
begin
FComponents.Free;
FComponents := nil;
end;
end;
procedure TComponent.InsertComponent(AComponent: TComponent);
begin
ValidateRename(AComponent, '', AComponent.FName);
Insert(AComponent);
AComponent.SetReference(True);
if csDesigning in ComponentState then
AComponent.SetDesigning(True);
Notification(AComponent, opInsert);
end;
procedure TComponent.RemoveComponent(AComponent: TComponent);
begin
Notification(AComponent, opRemove);
AComponent.SetReference(False);
Remove(AComponent);
AComponent.SetDesigning(False);
ValidateRename(AComponent, AComponent.FName, '');
end;
procedure TComponent.DestroyComponents;
var
Instance: TComponent;
begin
while FComponents <> nil do
begin
Instance := FComponents.Last;
Remove(Instance);
Instance.Destroy;
end;
end;
procedure TComponent.Destroying;
var
I: Integer;
begin
if not (csDestroying in FComponentState) then
begin
Include(FComponentState, csDestroying);
if FComponents <> nil then
for I := 0 to FComponents.Count - 1 do
TComponent(FComponents[I]).Destroying;
end;
end;
procedure TComponent.Notification(AComponent: TComponent;
Operation: TOperation);
var
I: Integer;
begin
if (FFreeNotifies <> nil) and (Operation = opRemove) then
begin
FFreeNotifies.Remove(AComponent);
if FFreeNotifies.Count = 0 then
begin
FFreeNotifies.Free;
FFreeNotifies := nil;
end;
end;
if FComponents <> nil then
for I := 0 to FComponents.Count - 1 do
TComponent(FComponents[I]).Notification(AComponent, Operation);
end;
procedure TComponent.DefineProperties(Filer: TFiler);
var
Ancestor: TComponent;
function DoWriteLeft: Boolean;
begin
if Ancestor <> nil then
Result := LongRec(FDesignInfo).Lo <> LongRec(Ancestor.FDesignInfo).Lo
else
Result := LongRec(FDesignInfo).Lo <> 0;
end;
function DoWriteTop: Boolean;
begin
if Ancestor <> nil then
Result := LongRec(FDesignInfo).Hi <> LongRec(Ancestor.FDesignInfo).Hi
else
Result := LongRec(FDesignInfo).Hi <> 0;
end;
begin
Ancestor := TComponent(Filer.Ancestor);
Filer.DefineProperty('Left', ReadLeft, WriteLeft, DoWriteLeft);
Filer.DefineProperty('Top', ReadTop, WriteTop, DoWriteTop);
end;
function TComponent.HasParent: Boolean;
begin
Result := False;
end;
procedure TComponent.GetChildren(Proc: TGetChildProc);
begin
end;
function TComponent.GetChildOwner: TComponent;
begin
Result := nil;
end;
function TComponent.GetChildParent: TComponent;
begin
Result := Self;
end;
procedure TComponent.SetChildOrder(Child: TComponent; Order: Integer);
begin
end;
function TComponent.GetParentComponent: TComponent;
begin
Result := nil;
end;
procedure TComponent.SetParentComponent(Value: TComponent);
begin
end;
procedure TComponent.Updating;
begin
Include(FComponentState, csUpdating);
end;
procedure TComponent.Updated;
begin
Exclude(FComponentState, csUpdating);
end;
procedure TComponent.Loaded;
begin
Exclude(FComponentState, csLoading);
end;
procedure TComponent.ReadState(Reader: TReader);
begin
Reader.ReadData(Self);
end;
procedure TComponent.WriteState(Writer: TWriter);
begin
Writer.WriteData(Self);
end;
procedure TComponent.ValidateRename(AComponent: TComponent;
const CurName, NewName: string);
begin
if (AComponent <> nil) and (CompareText(CurName, NewName) <> 0) and
(FindComponent(NewName) <> nil) then
raise EComponentError.CreateResFmt(SDuplicateName, [NewName]);
if (csDesigning in ComponentState) and (Owner <> nil) then
Owner.ValidateRename(AComponent, CurName, NewName);
end;
function TComponent.FindComponent(const AName: string): TComponent;
var
I: Integer;
begin
if (AName <> '') and (FComponents <> nil) then
for I := 0 to FComponents.Count - 1 do
begin
Result := FComponents[I];
if CompareText(Result.FName, AName) = 0 then Exit;
end;
Result := nil;
end;
procedure TComponent.SetName(const NewName: TComponentName);
begin
if FName <> NewName then
begin
if (NewName <> '') and not IsValidIdent(NewName) then
raise EComponentError.CreateResFmt(SInvalidName, [NewName]);
if FOwner <> nil then
FOwner.ValidateRename(Self, FName, NewName) else
ValidateRename(nil, FName, NewName);
SetReference(False);
ChangeName(NewName);
SetReference(True);
end;
end;
procedure TComponent.ChangeName(const NewName: TComponentName);
begin
FName := NewName;
end;
function TComponent.GetComponentIndex: Integer;
begin
if (FOwner <> nil) and (FOwner.FComponents <> nil) then
Result := FOwner.FComponents.IndexOf(Self) else
Result := -1;
end;
function TComponent.GetComponent(AIndex: Integer): TComponent;
begin
if FComponents = nil then ListError(SListIndexError);
Result := FComponents[AIndex];
end;
function TComponent.GetComponentCount: Integer;
begin
if FComponents <> nil then
Result := FComponents.Count else
Result := 0;
end;
procedure TComponent.SetComponentIndex(Value: Integer);
var
I, Count: Integer;
begin
if FOwner <> nil then
begin
I := FOwner.FComponents.IndexOf(Self);
if I >= 0 then
begin
Count := FOwner.FComponents.Count;
if Value < 0 then Value := 0;
if Value >= Count then Value := Count - 1;
if Value <> I then
begin
FOwner.FComponents.Delete(I);
FOwner.FComponents.Insert(Value, Self);
end;
end;
end;
end;
procedure TComponent.SetAncestor(Value: Boolean);
var
I: Integer;
begin
if Value then
Include(FComponentState, csAncestor) else
Exclude(FComponentState, csAncestor);
for I := 0 to ComponentCount - 1 do
Components[I].SetAncestor(Value);
end;
procedure TComponent.SetDesigning(Value: Boolean);
var
I: Integer;
begin
if Value then
Include(FComponentState, csDesigning) else
Exclude(FComponentState, csDesigning);
for I := 0 to ComponentCount - 1 do Components[I].SetDesigning(Value);
end;
procedure TComponent.SetReference(Enable: Boolean);
var
Field: ^TComponent;
begin
if FOwner <> nil then
begin
Field := FOwner.FieldAddress(FName);
if Field <> nil then
if Enable then Field^ := Self else Field^ := nil;
end;
end;
procedure FreeIntConstList;
var
I: Integer;
begin
for I := 0 to IntConstList.Count - 1 do
TIntConst(IntConstList[I]).Free;
IntConstList.Free;
end;
initialization
ClassList := TList.Create;
ClassAliasList := TStringList.Create;
IntConstList := TList.Create;
GlobalFixupList := TList.Create;
MainThreadID := GetCurrentThreadID;
GlobalLists := TList.Create;
finalization
ClassList.Free;
ClassAliasList.Free;
FreeIntConstList;
RemoveFixupReferences(nil, '');
GlobalFixupList.Free;
GlobalLists.Free;
end.