mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 08:38:14 +02:00
809 lines
22 KiB
ObjectPascal
809 lines
22 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal Integrated Development Environment
|
|
Copyright (c) 1998 by Berczi Gabor
|
|
|
|
Resource File support objects and routines
|
|
|
|
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 WResourc;
|
|
|
|
{$ifdef cpullvm}
|
|
{$modeswitch nestedprocvars}
|
|
{$endif}
|
|
{$H-}
|
|
|
|
interface
|
|
|
|
uses Objects;
|
|
|
|
const
|
|
TPDataBlockSignature = ord('F')+ord('B')*256;
|
|
ResourceBlockSignature = ord('R')+ord('D')*256;
|
|
|
|
langDefault = 0;
|
|
|
|
rcBinary = 1;
|
|
|
|
type
|
|
TResourceEntryHeader = packed record
|
|
ID : longint;
|
|
LangID : longint;
|
|
Flags : longint;
|
|
DataOfs: longint;
|
|
DataLen: sw_word;
|
|
end;
|
|
|
|
TResourceHeader = packed record
|
|
_Class : longint;
|
|
Flags : longint;
|
|
NameLen : word;
|
|
EntryCount : word;
|
|
end;
|
|
|
|
TResourceFileHeader = packed record
|
|
Signature : word;
|
|
InfoType : word;
|
|
InfoSize : longint;
|
|
{ ---- }
|
|
TableOfs : longint;
|
|
end;
|
|
|
|
PResourceEntry = ^TResourceEntry;
|
|
TResourceEntry = object(TObject)
|
|
constructor Init(AID, ALangID, AFlags, ADataLen: longint);
|
|
private
|
|
ID : longint;
|
|
LangID : longint;
|
|
Flags : longint;
|
|
DataOfs : longint;
|
|
DataLen : sw_word;
|
|
procedure BuildHeader(var Header : TResourceEntryHeader);
|
|
end;
|
|
|
|
PResourceEntryCollection = ^TResourceEntryCollection;
|
|
TResourceEntryCollection = object(TSortedCollection)
|
|
function At(Index: Sw_Integer): PResourceEntry;
|
|
function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
|
|
function SearchEntryForLang(ALangID: longint): PResourceEntry;
|
|
end;
|
|
|
|
PGlobalResourceEntryCollection = ^TGlobalResourceEntryCollection;
|
|
TGlobalResourceEntryCollection = object(TSortedCollection)
|
|
function At(Index: Sw_Integer): PResourceEntry;
|
|
function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
|
|
end;
|
|
|
|
PResource = ^TResource;
|
|
TResource = object(TObject)
|
|
constructor Init(const AName: string; AClass, AFlags: longint);
|
|
function GetName: string; virtual;
|
|
function FirstThatEntry(Func: TCallbackFunBoolParam): PResourceEntry; virtual;
|
|
procedure ForEachEntry(Func: TCallbackProcParam); virtual;
|
|
destructor Done; virtual;
|
|
private
|
|
Name : PString;
|
|
_Class : longint;
|
|
Flags : longint;
|
|
Items : PResourceEntryCollection;
|
|
procedure BuildHeader(var Header : TResourceHeader);
|
|
end;
|
|
|
|
TResourceCollection = object(TSortedCollection)
|
|
function At(Index: Sw_Integer): PResource;
|
|
function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
|
|
function SearchResourceByName(const AName: string): PResource;
|
|
end;
|
|
PResourceCollection = ^TResourceCollection;
|
|
|
|
TResourceFile = object(TObject)
|
|
constructor Init(var RS: TStream; ALoad: boolean);
|
|
constructor Create(var RS: TStream);
|
|
constructor Load(var RS: TStream);
|
|
constructor CreateFile(AFileName: string);
|
|
constructor LoadFile(AFileName: string);
|
|
function FirstThatResource(Func: TCallbackFunBoolParam): PResource; virtual;
|
|
procedure ForEachResource(Func: TCallbackProcParam); virtual;
|
|
procedure ForEachResourceEntry(Func: TCallbackProcParam); virtual;
|
|
function CreateResource(const Name: string; AClass, AFlags: longint): boolean; virtual;
|
|
function AddResourceEntry(const ResName: string; ALangID, AFlags: longint; var Data;
|
|
ADataSize: sw_integer): boolean; virtual;
|
|
function AddResourceEntryFromStream(const ResName: string; ALangID, AFlags: longint;
|
|
var Source: TStream; ADataSize: longint): boolean; virtual;
|
|
function DeleteResourceEntry(const ResName: string; ALangID: longint): boolean; virtual;
|
|
function DeleteResource(const ResName: string): boolean; virtual;
|
|
function ReadResourceEntry(const ResName: string; ALangID: longint; var Buf; BufSize: sw_word): boolean;
|
|
function ReadResourceEntryToStream(const ResName: string; ALangID: longint; var DestS: TStream): boolean;
|
|
procedure Flush; virtual;
|
|
destructor Done; virtual;
|
|
public
|
|
BaseOfs: longint;
|
|
function FindResource(const ResName: string): PResource;
|
|
function FindResourceEntry(const ResName: string; ALangID: longint): PResourceEntry;
|
|
private
|
|
S : PStream;
|
|
MyStream : boolean;
|
|
Resources : PResourceCollection;
|
|
Entries : PGlobalResourceEntryCollection;
|
|
Header : TResourceFileHeader;
|
|
Modified : boolean;
|
|
procedure UpdateBlockDatas;
|
|
function GetNextEntryID: longint;
|
|
function GetTotalSize(IncludeHeaders: boolean): longint;
|
|
function CalcSizes(IncludeHeaders, UpdatePosData: boolean): longint;
|
|
procedure AddResEntryPtr(P: PResource; E: PResourceEntry);
|
|
procedure RemoveResEntryPtr(P: PResource; E: PResourceEntry);
|
|
function DeleteArea(AreaStart, AreaSize, TotalSize: longint): boolean;
|
|
procedure BuildFileHeader;
|
|
procedure WriteHeader;
|
|
procedure WriteResourceTable;
|
|
end;
|
|
PResourceFile = ^TResourceFile;
|
|
|
|
implementation
|
|
|
|
uses
|
|
WUtils;
|
|
|
|
function TResourceEntryCollection.At(Index: Sw_Integer): PResourceEntry;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
function TResourceEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
|
|
var K1: PResourceEntry absolute Key1;
|
|
K2: PResourceEntry absolute Key2;
|
|
Re: Sw_integer;
|
|
begin
|
|
if K1^.LangID<K2^.LangID then Re:=-1 else
|
|
if K1^.LangID>K2^.LangID then Re:= 1 else
|
|
Re:=0;
|
|
Compare:=Re;
|
|
end;
|
|
|
|
function TResourceEntryCollection.SearchEntryForLang(ALangID: longint): PResourceEntry;
|
|
var P: PResourceEntry;
|
|
E: TResourceEntry;
|
|
Index: sw_integer;
|
|
begin
|
|
E.LangID:=ALangID;
|
|
if Search(@E,Index)=false then P:=nil else
|
|
P:=At(Index);
|
|
SearchEntryForLang:=P;
|
|
end;
|
|
|
|
function TGlobalResourceEntryCollection.At(Index: Sw_Integer): PResourceEntry;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
function TGlobalResourceEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
|
|
var K1: PResourceEntry absolute Key1;
|
|
K2: PResourceEntry absolute Key2;
|
|
Re: Sw_integer;
|
|
begin
|
|
if K1^.ID<K2^.ID then Re:=-1 else
|
|
if K1^.ID>K2^.ID then Re:= 1 else
|
|
Re:=0;
|
|
Compare:=Re;
|
|
end;
|
|
|
|
constructor TResourceEntry.Init(AID, ALangID, AFlags, ADataLen: longint);
|
|
begin
|
|
inherited Init;
|
|
ID:=AID;
|
|
LangID:=ALangID; Flags:=AFlags; DataLen:=ADataLen;
|
|
end;
|
|
|
|
procedure TResourceEntry.BuildHeader(var Header : TResourceEntryHeader);
|
|
begin
|
|
FillChar(Header,SizeOf(Header),0);
|
|
Header.ID:=ID;
|
|
Header.LangID:=LangID;
|
|
Header.Flags:=Flags;
|
|
Header.DataLen:=DataLen;
|
|
Header.DataOfs:=DataOfs;
|
|
end;
|
|
|
|
constructor TResource.Init(const AName: string; AClass, AFlags: longint);
|
|
begin
|
|
inherited Init;
|
|
Name:=NewStr(AName);
|
|
_Class:=AClass;
|
|
Flags:=AFlags;
|
|
New(Items, Init(10,50));
|
|
end;
|
|
|
|
function TResource.GetName: string;
|
|
begin
|
|
GetName:=GetStr(Name);
|
|
end;
|
|
|
|
function TResource.FirstThatEntry(Func: TCallbackFunBoolParam): PResourceEntry;
|
|
var EP,P: PResourceEntry;
|
|
I: sw_integer;
|
|
begin
|
|
P:=nil;
|
|
for I:=0 to Items^.Count-1 do
|
|
begin
|
|
EP:=Items^.At(I);
|
|
if Byte(Longint(CallPointerMethodLocal(Func,
|
|
get_caller_frame(get_frame,get_pc_addr),@Self,EP)))<>0 then
|
|
begin
|
|
P := EP;
|
|
Break;
|
|
end;
|
|
end;
|
|
FirstThatEntry:=P;
|
|
end;
|
|
|
|
procedure TResource.ForEachEntry(Func: TCallbackProcParam);
|
|
var RP: PResourceEntry;
|
|
I: sw_integer;
|
|
begin
|
|
for I:=0 to Items^.Count-1 do
|
|
begin
|
|
RP:=Items^.At(I);
|
|
CallPointerMethodLocal(Func,
|
|
get_caller_frame(get_frame,get_pc_addr),@Self,RP);
|
|
end;
|
|
end;
|
|
|
|
procedure TResource.BuildHeader(var Header : TResourceHeader);
|
|
begin
|
|
FillChar(Header,SizeOf(Header),0);
|
|
Header._Class:=_Class;
|
|
Header.Flags:=Flags;
|
|
Header.NameLen:=length(GetName);
|
|
Header.EntryCount:=Items^.Count;
|
|
end;
|
|
|
|
destructor TResource.Done;
|
|
begin
|
|
inherited Done;
|
|
if Name<>nil then DisposeStr(Name); Name:=nil;
|
|
if Items<>nil then Dispose(Items, Done); Items:=nil;
|
|
end;
|
|
|
|
function TResourceCollection.At(Index: Sw_Integer): PResource;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
function TResourceCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
|
|
var K1: PResource absolute Key1;
|
|
K2: PResource absolute Key2;
|
|
N1,N2: string;
|
|
Re: Sw_integer;
|
|
begin
|
|
N1:=UpcaseStr(K1^.GetName); N2:=UpcaseStr(K2^.GetName);
|
|
if N1<N2 then Re:=-1 else
|
|
if N1>N2 then Re:= 1 else
|
|
Re:=0;
|
|
Compare:=Re;
|
|
end;
|
|
|
|
function TResourceCollection.SearchResourceByName(const AName: string): PResource;
|
|
var P,R: PResource;
|
|
Index: sw_integer;
|
|
begin
|
|
New(R, Init(AName,0,0));
|
|
if Search(R,Index)=false then P:=nil else
|
|
P:=At(Index);
|
|
Dispose(R, Done);
|
|
SearchResourceByName:=P;
|
|
end;
|
|
|
|
constructor TResourceFile.Create(var RS: TStream);
|
|
begin
|
|
if Init(RS,false)=false then
|
|
Fail;
|
|
end;
|
|
|
|
constructor TResourceFile.Load(var RS: TStream);
|
|
begin
|
|
if Init(RS,true)=false then
|
|
Fail;
|
|
end;
|
|
|
|
constructor TResourceFile.Init(var RS: TStream; ALoad: boolean);
|
|
var OK: boolean;
|
|
RH: TResourceHeader;
|
|
REH: TResourceEntryHeader;
|
|
EndPos,I: longint;
|
|
P: PResource;
|
|
E: PResourceEntry;
|
|
St: string;
|
|
begin
|
|
inherited Init;
|
|
S:=@RS;
|
|
New(Resources, Init(100, 1000));
|
|
New(Entries, Init(500,2000));
|
|
OK:=true;
|
|
if ALoad=false then
|
|
Modified:=true
|
|
else
|
|
begin
|
|
S^.Reset;
|
|
BaseOfs:=S^.GetPos;
|
|
S^.Read(Header,SizeOf(Header));
|
|
OK:=(S^.Status=stOK) and
|
|
(Header.Signature=TPDataBlockSignature) and
|
|
(Header.InfoType=ResourceBlockSignature);
|
|
if OK then begin S^.Seek(BaseOfs+Header.TableOfs); OK:=S^.Status=stOK; end;
|
|
EndPos:=BaseOfs+Header.InfoSize;
|
|
if OK then
|
|
while OK and (S^.GetPos<EndPos) do
|
|
begin
|
|
S^.Read(RH,SizeOf(RH)); OK:=(S^.Status=stOK);
|
|
if OK then begin St[0]:=chr(RH.NameLen); S^.Read(St[1],RH.NameLen); OK:=(S^.Status=stOK); end;
|
|
if OK then
|
|
begin
|
|
New(P, Init(St,RH._Class,RH.Flags));
|
|
Resources^.Insert(P);
|
|
end;
|
|
I:=0;
|
|
while OK and (I<RH.EntryCount) do
|
|
begin
|
|
S^.Read(REH,SizeOf(REH)); OK:=(S^.Status=stOK);
|
|
if OK then
|
|
begin
|
|
New(E, Init(REH.ID,REH.LangID,REH.Flags,REH.DataLen));
|
|
AddResEntryPtr(P,E);
|
|
end;
|
|
if OK then Inc(I);
|
|
end;
|
|
if OK then UpdateBlockDatas;
|
|
end;
|
|
end;
|
|
if OK=false then
|
|
begin
|
|
Done;
|
|
Fail;
|
|
end;
|
|
end;
|
|
|
|
function TResourceFile.FirstThatResource(Func: TCallbackFunBoolParam): PResource;
|
|
var RP,P: PResource;
|
|
I: sw_integer;
|
|
begin
|
|
P:=nil;
|
|
for I:=0 to Resources^.Count-1 do
|
|
begin
|
|
RP:=Resources^.At(I);
|
|
if Byte(Longint(CallPointerMethodLocal(Func,
|
|
get_caller_frame(get_frame,get_pc_addr),@Self,RP)))<>0 then
|
|
begin
|
|
P := RP;
|
|
Break;
|
|
end;
|
|
end;
|
|
FirstThatResource:=P;
|
|
end;
|
|
|
|
procedure TResourceFile.ForEachResource(Func: TCallbackProcParam);
|
|
var RP: PResource;
|
|
I: sw_integer;
|
|
begin
|
|
for I:=0 to Resources^.Count-1 do
|
|
begin
|
|
RP:=Resources^.At(I);
|
|
CallPointerMethodLocal(Func,get_caller_frame(get_frame,get_pc_addr),@Self,RP);
|
|
end;
|
|
end;
|
|
|
|
procedure TResourceFile.ForEachResourceEntry(Func: TCallbackProcParam);
|
|
var E: PResourceEntry;
|
|
I: sw_integer;
|
|
begin
|
|
for I:=0 to Entries^.Count-1 do
|
|
begin
|
|
E:=Entries^.At(I);
|
|
CallPointerMethodLocal(Func,get_caller_frame(get_frame,get_pc_addr),@Self,E);
|
|
end;
|
|
end;
|
|
|
|
function TResourceFile.CreateResource(const Name: string; AClass, AFlags: longint): boolean;
|
|
var OK: boolean;
|
|
P: PResource;
|
|
begin
|
|
OK:=FindResource(Name)=nil;
|
|
if OK then
|
|
begin
|
|
New(P, Init(Name,AClass,AFlags));
|
|
Resources^.Insert(P);
|
|
Modified:=true;
|
|
end;
|
|
CreateResource:=OK;
|
|
end;
|
|
|
|
function TResourceFile.AddResourceEntry(const ResName: string; ALangID, AFlags: longint; var Data;
|
|
ADataSize: sw_integer): boolean;
|
|
const BlockSize = 4096;
|
|
var OK: boolean;
|
|
P: PResource;
|
|
E: PResourceEntry;
|
|
RemSize,CurOfs,FragSize: longint;
|
|
begin
|
|
P:=FindResource(ResName);
|
|
OK:=P<>nil;
|
|
if OK then
|
|
OK:=(P^.Items^.SearchEntryForLang(ALangID)=nil);
|
|
if OK then
|
|
begin
|
|
New(E, Init(GetNextEntryID,ALangID, AFlags, ADataSize));
|
|
AddResEntryPtr(P,E);
|
|
UpdateBlockDatas;
|
|
RemSize:=ADataSize; CurOfs:=0;
|
|
S^.Reset;
|
|
S^.Seek(BaseOfs+E^.DataOfs);
|
|
while (RemSize>0) do
|
|
begin
|
|
FragSize:=Min(RemSize,BlockSize);
|
|
S^.Write(PByteArray(@Data)^[CurOfs],FragSize);
|
|
Dec(RemSize,FragSize); Inc(CurOfs,FragSize);
|
|
end;
|
|
Modified:=true;
|
|
end;
|
|
AddResourceEntry:=OK;
|
|
end;
|
|
|
|
function TResourceFile.AddResourceEntryFromStream(const ResName: string; ALangID, AFlags: longint;
|
|
var Source: TStream; ADataSize: longint): boolean;
|
|
const BufSize = 4096;
|
|
var OK: boolean;
|
|
P: PResource;
|
|
E: PResourceEntry;
|
|
RemSize,FragSize: longint;
|
|
Buf: pointer;
|
|
begin
|
|
P:=FindResource(ResName);
|
|
OK:=P<>nil;
|
|
if OK then
|
|
OK:=(P^.Items^.SearchEntryForLang(ALangID)=nil);
|
|
if OK then
|
|
begin
|
|
New(E, Init(GetNextEntryID, ALangID, AFlags, ADataSize));
|
|
AddResEntryPtr(P,E);
|
|
UpdateBlockDatas;
|
|
GetMem(Buf,BufSize);
|
|
RemSize:=ADataSize;
|
|
S^.Reset;
|
|
S^.Seek(BaseOfs+E^.DataOfs);
|
|
while (RemSize>0) do
|
|
begin
|
|
FragSize:=Min(RemSize,BufSize);
|
|
Source.Read(Buf^,FragSize);
|
|
S^.Write(Buf^,FragSize);
|
|
Dec(RemSize,FragSize);
|
|
end;
|
|
FreeMem(Buf,BufSize);
|
|
Modified:=true;
|
|
end;
|
|
AddResourceEntryFromStream:=OK;
|
|
end;
|
|
|
|
function TResourceFile.DeleteResourceEntry(const ResName: string; ALangID: longint): boolean;
|
|
var E: PResourceEntry;
|
|
P: PResource;
|
|
OK: boolean;
|
|
begin
|
|
P:=FindResource(ResName);
|
|
OK:=P<>nil;
|
|
if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
|
|
OK:=OK and (E<>nil);
|
|
if OK then
|
|
begin
|
|
OK:=DeleteArea(E^.DataOfs,E^.DataLen,GetTotalSize(false));
|
|
if OK then begin RemoveResEntryPtr(P,E); Dispose(E, Done); end;
|
|
Modified:=true;
|
|
end;
|
|
DeleteResourceEntry:=OK;
|
|
end;
|
|
|
|
function TResourceFile.DeleteResource(const ResName: string): boolean;
|
|
var P: PResource;
|
|
E: PResourceEntry;
|
|
OK: boolean;
|
|
begin
|
|
P:=FindResource(ResName);
|
|
OK:=P<>nil;
|
|
if P<>nil then
|
|
begin
|
|
while OK and (P^.Items^.Count>0) do
|
|
begin
|
|
E:=P^.Items^.At(P^.Items^.Count-1);
|
|
OK:=OK and DeleteResourceEntry(ResName,E^.LangID);
|
|
end;
|
|
Modified:=true;
|
|
end;
|
|
if OK then Resources^.Free(P);
|
|
DeleteResource:=OK;
|
|
end;
|
|
|
|
function TResourceFile.ReadResourceEntry(const ResName: string; ALangID: longint; var Buf; BufSize: sw_word): boolean;
|
|
var E: PResourceEntry;
|
|
P: PResource;
|
|
OK: boolean;
|
|
CurOfs,CurFrag: sw_word;
|
|
TempBuf: pointer;
|
|
const TempBufSize = 4096;
|
|
begin
|
|
E:=nil;
|
|
P:=FindResource(ResName);
|
|
OK:=P<>nil;
|
|
if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
|
|
OK:=OK and (E<>nil);
|
|
OK:=OK and (E^.DataLen<=BufSize);
|
|
if OK then
|
|
begin
|
|
GetMem(TempBuf,TempBufSize);
|
|
S^.Reset;
|
|
S^.Seek(BaseOfs+E^.DataOfs);
|
|
OK:=(S^.Status=stOK);
|
|
CurOfs:=0;
|
|
|
|
while OK and (CurOfs<E^.DataLen) do
|
|
begin
|
|
CurFrag:=Min(E^.DataLen-CurOfs,TempBufSize);
|
|
S^.Read(TempBuf^,CurFrag);
|
|
OK:=OK and (S^.Status=stOK);
|
|
if OK then
|
|
Move(TempBuf^,PByteArray(@Buf)^[CurOfs],CurFrag);
|
|
Inc(CurOfs,CurFrag);
|
|
end;
|
|
|
|
FreeMem(TempBuf,TempBufSize);
|
|
end;
|
|
ReadResourceEntry:=OK;
|
|
end;
|
|
|
|
function TResourceFile.ReadResourceEntryToStream(const ResName: string; ALangID: longint; var DestS: TStream): boolean;
|
|
var E: PResourceEntry;
|
|
P: PResource;
|
|
OK: boolean;
|
|
CurOfs,CurFrag: sw_word;
|
|
TempBuf: pointer;
|
|
const TempBufSize = 4096;
|
|
begin
|
|
P:=FindResource(ResName);
|
|
OK:=P<>nil;
|
|
if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
|
|
OK:=OK and (E<>nil);
|
|
if OK then
|
|
begin
|
|
GetMem(TempBuf,TempBufSize);
|
|
S^.Reset;
|
|
S^.Seek(BaseOfs+E^.DataOfs);
|
|
OK:=(S^.Status=stOK);
|
|
CurOfs:=0;
|
|
{ this results sometimes in endless loops
|
|
when the resource are changed PM }
|
|
if E^.DataLen<0 then
|
|
OK:=false;
|
|
while OK and (CurOfs<E^.DataLen) do
|
|
begin
|
|
CurFrag:=Min(E^.DataLen-CurOfs,TempBufSize);
|
|
S^.Read(TempBuf^,CurFrag);
|
|
OK:=OK and (S^.Status=stOK);
|
|
if OK then
|
|
DestS.Write(TempBuf^,CurFrag);
|
|
OK:=OK and (DestS.Status=stOK);
|
|
Inc(CurOfs,CurFrag);
|
|
end;
|
|
|
|
FreeMem(TempBuf,TempBufSize);
|
|
end;
|
|
ReadResourceEntryToStream:=OK;
|
|
end;
|
|
|
|
function TResourceFile.FindResource(const ResName: string): PResource;
|
|
begin
|
|
FindResource:=Resources^.SearchResourceByName(ResName);
|
|
end;
|
|
|
|
function TResourceFile.FindResourceEntry(const ResName: string; ALangID: longint): PResourceEntry;
|
|
var P: PResource;
|
|
E: PResourceEntry;
|
|
begin
|
|
E:=nil;
|
|
P:=FindResource(ResName);
|
|
if P<>nil then
|
|
E:=P^.Items^.SearchEntryForLang(ALangID);
|
|
FindResourceEntry:=E;
|
|
end;
|
|
|
|
procedure TResourceFile.Flush;
|
|
begin
|
|
if Modified=false then Exit;
|
|
BuildFileHeader;
|
|
S^.Seek(BaseOfs);
|
|
WriteHeader;
|
|
S^.Seek(BaseOfs+Header.TableOfs);
|
|
WriteResourceTable;
|
|
S^.Truncate;
|
|
Modified:=false;
|
|
end;
|
|
|
|
procedure TResourceFile.BuildFileHeader;
|
|
begin
|
|
FillChar(Header,SizeOf(Header),0);
|
|
with Header do
|
|
begin
|
|
Signature:=TPDataBlockSignature;
|
|
InfoType:=ResourceBlockSignature;
|
|
InfoSize:=GetTotalSize(true);
|
|
TableOfs:=GetTotalSize(false);
|
|
end;
|
|
end;
|
|
|
|
procedure TResourceFile.WriteHeader;
|
|
begin
|
|
S^.Write(Header,SizeOf(Header));
|
|
end;
|
|
|
|
procedure TResourceFile.WriteResourceTable;
|
|
var RH: TResourceHeader;
|
|
REH: TResourceEntryHeader;
|
|
procedure WriteResource(P: PResource);
|
|
procedure WriteResourceEntry(P: PResourceEntry);
|
|
begin
|
|
P^.BuildHeader(REH);
|
|
S^.Write(REH,SizeOf(REH));
|
|
end;
|
|
var N: string;
|
|
begin
|
|
if P^.Items^.Count=0 then Exit; { do not store resources with no entries }
|
|
P^.BuildHeader(RH);
|
|
S^.Write(RH,SizeOf(RH));
|
|
N:=P^.GetName;
|
|
S^.Write(N[1],length(N));
|
|
P^.ForEachEntry(TCallbackProcParam(@WriteResourceEntry));
|
|
end;
|
|
begin
|
|
ForEachResource(TCallbackProcParam(@WriteResource));
|
|
end;
|
|
|
|
procedure TResourceFile.UpdateBlockDatas;
|
|
begin
|
|
CalcSizes(false,true);
|
|
end;
|
|
|
|
function TResourceFile.GetTotalSize(IncludeHeaders: boolean): longint;
|
|
begin
|
|
GetTotalSize:=CalcSizes(IncludeHeaders,false);
|
|
end;
|
|
|
|
function TResourceFile.CalcSizes(IncludeHeaders, UpdatePosData: boolean): longint;
|
|
var RH : TResourceHeader;
|
|
REH : TResourceEntryHeader;
|
|
Size: longint;
|
|
NamesSize: longint;
|
|
procedure AddResourceEntrySize(P: PResourceEntry);
|
|
begin
|
|
if UpdatePosData then P^.DataOfs:=Size;
|
|
P^.BuildHeader(REH);
|
|
Inc(Size,REH.DataLen);
|
|
end;
|
|
procedure AddResourceSize(P: PResource);
|
|
var RH: TResourceHeader;
|
|
begin
|
|
P^.BuildHeader(RH);
|
|
Inc(NamesSize,RH.NameLen);
|
|
end;
|
|
begin
|
|
Size:=0; NamesSize:=0;
|
|
Inc(Size,SizeOf(Header)); { this is on start so we always include it }
|
|
ForEachResourceEntry(TCallbackProcParam(@AddResourceEntrySize));
|
|
if IncludeHeaders then
|
|
begin
|
|
ForEachResource(TCallbackProcParam(@AddResourceSize));
|
|
Inc(Size,SizeOf(RH)*Resources^.Count);
|
|
Inc(Size,SizeOf(REH)*Entries^.Count);
|
|
Inc(Size,NamesSize);
|
|
end;
|
|
CalcSizes:=Size;
|
|
end;
|
|
|
|
function TResourceFile.DeleteArea(AreaStart, AreaSize, TotalSize: longint): boolean;
|
|
const BufSize = 4096;
|
|
var RemSize,FragSize,CurOfs: longint;
|
|
Buf: pointer;
|
|
OK: boolean;
|
|
begin
|
|
GetMem(Buf,BufSize);
|
|
RemSize:=TotalSize-(AreaStart+AreaSize); CurOfs:=0;
|
|
OK:=RemSize>=0;
|
|
while (RemSize>0) do
|
|
begin
|
|
FragSize:=Min(RemSize,BufSize);
|
|
S^.Seek(BaseOfs+AreaStart+AreaSize+CurOfs);
|
|
S^.Read(Buf^,BufSize);
|
|
OK:=OK and (S^.Status=stOK);
|
|
if OK then
|
|
begin
|
|
S^.Seek(BaseOfs+AreaStart+CurOfs);
|
|
S^.Write(Buf^,BufSize);
|
|
OK:=OK and (S^.Status=stOK);
|
|
end;
|
|
Inc(CurOfs,FragSize); Dec(RemSize,FragSize);
|
|
end;
|
|
FreeMem(Buf,BufSize);
|
|
DeleteArea:=OK;
|
|
end;
|
|
|
|
procedure TResourceFile.AddResEntryPtr(P: PResource; E: PResourceEntry);
|
|
begin
|
|
if (P=nil) or (E=nil) then Exit;
|
|
P^.Items^.Insert(E);
|
|
Entries^.Insert(E);
|
|
end;
|
|
|
|
procedure TResourceFile.RemoveResEntryPtr(P: PResource; E: PResourceEntry);
|
|
begin
|
|
if (P=nil) or (E=nil) then Exit;
|
|
Entries^.Delete(E);
|
|
P^.Items^.Delete(E);
|
|
end;
|
|
|
|
function TResourceFile.GetNextEntryID: longint;
|
|
var ID: longint;
|
|
begin
|
|
if Entries^.Count=0 then ID:=1 else
|
|
ID:=Entries^.At(Entries^.Count-1)^.ID+1;
|
|
GetNextEntryID:=ID;
|
|
end;
|
|
|
|
destructor TResourceFile.Done;
|
|
begin
|
|
Flush;
|
|
inherited Done;
|
|
{ if assigned(S) then dispose(S,Done); S:=nil;}
|
|
if Resources<>nil then Dispose(Resources, Done); Resources:=nil;
|
|
if Entries<>nil then
|
|
begin Entries^.DeleteAll; Dispose(Entries, Done); Entries:=nil; end;
|
|
if MyStream and Assigned(S) then
|
|
Dispose(S, Done);
|
|
end;
|
|
|
|
constructor TResourceFile.CreateFile(AFileName: string);
|
|
var B: PFastBufStream;
|
|
begin
|
|
New(B, Init(AFileName, stCreate, 4096));
|
|
if (B<>nil) and (B^.Status<>stOK) then
|
|
begin Dispose(B, Done); B:=nil; end;
|
|
if B=nil then Fail;
|
|
if Create(B^)=false then
|
|
Begin
|
|
Dispose(B,Done);
|
|
Fail;
|
|
End;
|
|
MyStream:=true;
|
|
{$ifdef HASAMIGA}
|
|
Flush;
|
|
{$endif}
|
|
end;
|
|
|
|
constructor TResourceFile.LoadFile(AFileName: string);
|
|
var B: PFastBufStream;
|
|
begin
|
|
New(B, Init(AFileName, stOpenRead, 4096));
|
|
if (B<>nil) and (B^.Status<>stOK) then
|
|
begin Dispose(B, Done); B:=nil; end;
|
|
if B=nil then Fail;
|
|
if Load(B^)=false then
|
|
Begin
|
|
Dispose(B,Done);
|
|
Fail;
|
|
End;
|
|
MyStream:=true;
|
|
end;
|
|
|
|
END.
|