Repository URL to install this package:
|
Version:
3.0.0 ▾
|
{
This file is part of the Free Pascal run time library.
Copyright (c) 2008 by Giulio Bernardi
Implements an ordered tree of resources
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit resourcetree;
{$MODE OBJFPC}
interface
uses
Classes, SysUtils, resource;
type
{ TResourceTreeNode }
TResourceTreeNode = class
protected
fParent : TResourceTreeNode;
fNamedEntries : TFPList;
fIDEntries : TFPList;
fSubDirRVA : longword;
fDataRVA : longword;
fNameRva : longword;
fDesc : TResourceDesc;
function GetNamedCount : longword;
function GetNamedEntry(index : integer) : TResourceTreeNode;
function GetIDCount : longword;
function GetIDEntry(index : integer) : TResourceTreeNode;
function GetData : TAbstractResource; virtual;
function InternalFind(aList : TFPList; aDesc : TResourceDesc; out index : integer) : boolean; overload;
function InternalFind(aList : TFPList; aLangID : TLangID; out index : integer) : boolean; overload;
function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; virtual; abstract; overload;
constructor Create; virtual; overload;
property Parent : TResourceTreeNode read fParent;
public
destructor Destroy; override;
procedure Add(aResource : TAbstractResource); virtual; abstract;
function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; virtual; abstract;
function CreateResource : TAbstractResource; virtual;
procedure Clear;
function Remove(aType, aName : TResourceDesc) : TAbstractResource; overload;
function Remove(aType, aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
function Find(aType, aName : TResourceDesc) : TAbstractResource; overload;
function Find(aType, aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
function FindFreeID(aType : TResourceDesc) : TResID; virtual;
function IsLeaf : boolean; virtual;
property Desc : TResourceDesc read fDesc;
property NamedCount : longword read GetNamedCount;
property NamedEntries[index : integer] : TResourceTreeNode read GetNamedEntry;
property IDCount : longword read GetIDCount;
property IDEntries[index : integer] : TResourceTreeNode read GetIDEntry;
property NameRVA : longword read fNameRVA write fNameRVA;
property SubDirRVA : longword read fSubDirRVA write fSubDirRVA;
property DataRVA : longword read fDataRVA write fDataRVA;
property Data : TAbstractResource read GetData;
end;
{ TRootResTreeNode }
TRootResTreeNode = class (TResourceTreeNode)
protected
function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
public
constructor Create; override;
function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
procedure Add(aResource : TAbstractResource); override;
function FindFreeID(aType : TResourceDesc) : TResID; override;
end;
implementation
uses resfactory;
{ TTypeResTreeNode }
type
TTypeResTreeNode = class (TResourceTreeNode)
protected
function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
public
constructor Create(aType : TResourceDesc; aParent : TResourceTreeNode); overload;
function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
procedure Add(aResource : TAbstractResource); override;
function FindFreeID(aType : TResourceDesc) : TResID; override;
end;
{ TNameResTreeNode }
TNameResTreeNode = class (TResourceTreeNode)
protected
function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
public
constructor Create(aName : TResourceDesc; aParent : TResourceTreeNode); overload;
function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
procedure Add(aResource : TAbstractResource); override;
end;
{ TLangIDResTreeNode }
TLangIDResTreeNode = class (TResourceTreeNode)
private
fData : TAbstractResource;
protected
function GetData : TAbstractResource; override;
function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
public
constructor Create(aLangID : TLangID; aResource : TAbstractResource; aParent : TResourceTreeNode); overload;
function CreateResource : TAbstractResource; override;
function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
procedure Add(aResource : TAbstractResource); override;
function IsLeaf : boolean; override;
end;
function CompareDesc(desc1, desc2 : TResourceDesc) : integer;
begin
if desc1.DescType=desc2.DescType then
begin
case desc1.DescType of
dtID : Result:=desc1.ID - desc2.ID;
dtName : Result:=CompareStr(desc1.Name,desc2.Name);
end;
end
else
case desc1.DescType of
dtID : Result:=1;
dtName : Result:=-1;
end;
end;
function ResListCompare(Item1: Pointer;Item2: Pointer) : Integer;
var node1, node2 : TResourceTreeNode;
begin
node1:=TResourceTreeNode(Item1);
node2:=TResourceTreeNode(Item2);
Result:=CompareDesc(node1.Desc,node2.Desc);
end;
{ TResourceTreeNode }
function TResourceTreeNode.GetIDEntry(index : integer): TResourceTreeNode;
begin
Result:=TResourceTreeNode(fIDEntries[index]);
end;
function TResourceTreeNode.GetData: TAbstractResource;
begin
Result:=nil;
end;
function TResourceTreeNode.InternalFind(aList: TFPList; aDesc: TResourceDesc; out index: integer
): boolean;
var l, r, p,res : integer;
begin
Result:=true;
l:=0;
r:=aList.Count-1;
while l<=r do
begin
p:=(l+r) div 2;
res:=CompareDesc(TResourceTreeNode(aList[p]).Desc, aDesc);
if res<0 then l:=p+1
else if res>0 then r:=p-1
else if res=0 then
begin
index:=p;
exit;
end;
end;
index:=l; //the item can be inserted here
Result:=false;
end;
function TResourceTreeNode.InternalFind(aList: TFPList; aLangID: TLangID; out
index: integer): boolean;
var l, r, p,res : integer;
begin
Result:=true;
l:=0;
r:=aList.Count-1;
while l<=r do
begin
p:=(l+r) div 2;
if TResourceTreeNode(aList[p]).Desc.DescType=dtName then res:=-1
else res:=TResourceTreeNode(aList[p]).Desc.ID - aLangID;
if res<0 then l:=p+1
else if res>0 then r:=p-1
else if res=0 then
begin
index:=p;
exit;
end;
end;
index:=l; //the item can be inserted here
Result:=false;
end;
function TResourceTreeNode.GetNamedEntry(index : integer): TResourceTreeNode;
begin
Result:=TResourceTreeNode(fNamedEntries[index]);
end;
function TResourceTreeNode.GetNamedCount: longword;
begin
Result:=fNamedEntries.Count;
end;
function TResourceTreeNode.GetIDCount: longword;
begin
Result:=fIDEntries.Count;
end;
constructor TResourceTreeNode.Create;
begin
fDesc:=TResourceDesc.Create(0);
fNamedEntries:=TFPList.Create;
fIDEntries:=TFPList.Create;
fNameRVA:=0;
fSubDirRva:=0;
fDataRVA:=0;
fParent:=nil;
end;
destructor TResourceTreeNode.Destroy;
begin
fDesc.Free;
Clear;
fNamedEntries.Free;
fIDEntries.Free;
end;
function TResourceTreeNode.CreateResource: TAbstractResource;
begin
Result:=nil;
end;
procedure TResourceTreeNode.Clear;
var i : integer;
begin
for i:=0 to fNamedEntries.Count-1 do
TResourceTreeNode(fNamedEntries[i]).Free;
for i:=0 to fIDEntries.Count-1 do
TResourceTreeNode(fIDEntries[i]).Free;
fNamedEntries.Clear;
fIDEntries.Clear;
end;
function TResourceTreeNode.Remove(aType, aName: TResourceDesc
): TAbstractResource;
begin
Result:=InternalFind(aType,aName,0,true,true);
end;
function TResourceTreeNode.Remove(aType, aName: TResourceDesc;
const aLangID: TLangID): TAbstractResource;
begin
Result:=InternalFind(aType,aName,aLangID,false,true);
end;
function TResourceTreeNode.Find(aType, aName: TResourceDesc
): TAbstractResource;
begin
Result:=InternalFind(aType,aName,0,true,false);
end;
function TResourceTreeNode.Find(aType, aName: TResourceDesc;
const aLangID: TLangID): TAbstractResource;
begin
Result:=InternalFind(aType,aName,aLangID,false,false);
end;
function TResourceTreeNode.FindFreeID(aType: TResourceDesc): TResID;
begin
Result:=0;
end;
function TResourceTreeNode.IsLeaf: boolean;
begin
Result:=false;
end;
{ TRootResTreeNode }
constructor TRootResTreeNode.Create;
begin
inherited Create;
end;
function TRootResTreeNode.CreateSubNode(aDesc: TResourceDesc
): TResourceTreeNode;
var theList : TFPList;
begin
case aDesc.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
Result:=TTypeResTreeNode.Create(aDesc,self);
thelist.Add(Result);
end;
procedure TRootResTreeNode.Add(aResource: TAbstractResource);
var theList : TFPList;
idx : integer;
subitem : TResourceTreeNode;
begin
case aResource._Type.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
if InternalFind(theList,aResource._Type,idx) then
subitem:=TResourceTreeNode(theList[idx])
else
begin
subitem:=TTypeResTreeNode.Create(aResource._Type,self);
theList.Insert(idx,subitem);
end;
subitem.Add(aResource);
end;
function TRootResTreeNode.FindFreeID(aType: TResourceDesc): TResID;
var theList : TFPList;
idx : integer;
subitem : TResourceTreeNode;
begin
Result:=1;
case aType.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
if InternalFind(theList,aType,idx) then
subitem:=TResourceTreeNode(theList[idx])
else exit;
Result:=subitem.FindFreeID(aType);
end;
function TRootResTreeNode.InternalFind(aType, aName : TResourceDesc;
const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
var theList : TFPList;
idx : integer;
subitem : TResourceTreeNode;
begin
Result:=nil;
case aType.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
if InternalFind(theList,aType,idx) then
subitem:=TResourceTreeNode(theList[idx])
else exit;
Result:=subitem.InternalFind(aType,aName,aLangID,noLangID,toDelete);
if toDelete and ((subitem.IDCount+subitem.NamedCount)=0) then
begin
subitem.Free;
theList.Delete(idx);
end;
end;
{ TTypeResTreeNode }
constructor TTypeResTreeNode.Create(aType: TResourceDesc;
aParent: TResourceTreeNode);
begin
inherited Create;
fDesc.Assign(aType);
fParent:=aParent;
end;
function TTypeResTreeNode.CreateSubNode(aDesc: TResourceDesc
): TResourceTreeNode;
var theList : TFPList;
begin
case aDesc.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
Result:=TNameResTreeNode.Create(aDesc,self);
thelist.Add(Result);
end;
procedure TTypeResTreeNode.Add(aResource: TAbstractResource);
var theList : TFPList;
idx : integer;
subitem : TResourceTreeNode;
begin
case aResource.Name.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
if InternalFind(theList,aResource.Name,idx) then
subitem:=TResourceTreeNode(theList[idx])
else
begin
subitem:=TNameResTreeNode.Create(aResource.Name,self);
theList.Insert(idx,subitem);
end;
subitem.Add(aResource);
end;
function TTypeResTreeNode.FindFreeID(aType: TResourceDesc): TResID;
var i : integer;
begin
Result:=1;
if IDCount<=0 then exit; //no items, use 1
Result:=IDEntries[IDCount-1].Desc.ID+1; //try last one+1
if Result>$FFFF then
if IDEntries[0].Desc.ID>1 then Result:=IDEntries[0].Desc.ID-1 //try first one-1
else
begin //scan the whole list to find the first free id.
Result:=1;
for i:=0 to IDCount-1 do
begin
if IDEntries[i].Desc.ID<>Result then exit;
inc(Result);
end;
raise ENoMoreFreeIDsException.Create('');
end;
end;
function TTypeResTreeNode.InternalFind(aType, aName : TResourceDesc;
const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
var theList : TFPList;
idx : integer;
subitem : TResourceTreeNode;
begin
Result:=nil;
case aName.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
if InternalFind(theList,aName,idx) then
subitem:=TResourceTreeNode(theList[idx])
else exit;
Result:=subitem.InternalFind(aType,aName,aLangID,noLangID,toDelete);
if toDelete and ((subitem.IDCount+subitem.NamedCount)=0) then
begin
subitem.Free;
theList.Delete(idx);
end;
end;
{ TNameResTreeNode }
constructor TNameResTreeNode.Create(aName: TResourceDesc;
aParent: TResourceTreeNode);
begin
inherited Create;
fDesc.Assign(aName);
fParent:=aParent;
end;
function TNameResTreeNode.CreateSubNode(aDesc: TResourceDesc
): TResourceTreeNode;
var theList : TFPList;
begin
case aDesc.DescType of
dtID : theList:=fIDEntries;
dtName : theList:=fNamedEntries;
end;
Result:=TLangIDResTreeNode.Create(aDesc.ID,nil,self);
thelist.Add(Result);
end;
procedure TNameResTreeNode.Add(aResource: TAbstractResource);
var idx : integer;
subitem : TResourceTreeNode;
begin
if InternalFind(fIDEntries,aResource.LangID,idx) then
raise EResourceDuplicateException.Create('');
subitem:=TLangIDResTreeNode.Create(aResource.LangID,aResource,self);
fIDEntries.Insert(idx,subitem);
end;
function TNameResTreeNode.InternalFind(aType, aName : TResourceDesc;
const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
var idx : integer;
begin
Result:=nil;
if noLangID then
begin
if IDCount<=0 then exit
else idx:=0;
end
else
if not InternalFind(fIDEntries,aLangID,idx) then exit;
Result:=IDEntries[idx].Data;
if toDelete then
begin
IDEntries[idx].Free;
fIDEntries.Delete(idx);
end;
end;
{ TLangIDResTreeNode }
function TLangIDResTreeNode.GetData: TAbstractResource;
begin
Result:=fData;
end;
constructor TLangIDResTreeNode.Create(aLangID: TLangID;
aResource: TAbstractResource; aParent: TResourceTreeNode);
begin
inherited Create;
fDesc.ID:=aLangID;
fData:=aResource;
fParent:=aParent;
end;
function TLangIDResTreeNode.CreateResource: TAbstractResource;
var theType, theName : TResourceDesc;
begin
Result:=nil;
if fData<>nil then exit;
theType:=Parent.Parent.Desc;
theName:=Parent.Desc;
fData:=TResourceFactory.CreateResource(theType,theName);
fData.LangID:=fDesc.ID;
Result:=fData;
end;
function TLangIDResTreeNode.CreateSubNode(aDesc: TResourceDesc
): TResourceTreeNode;
begin
Result:=nil;
end;
procedure TLangIDResTreeNode.Add(aResource: TAbstractResource);
begin
//can't add, it's a leaf node
end;
function TLangIDResTreeNode.InternalFind(aType, aName : TResourceDesc;
const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
begin
//can't find, it's a leaf node
Result:=nil;
end;
function TLangIDResTreeNode.IsLeaf: boolean;
begin
Result:=true;
end;
end.