Files
kam-mobile/vendor/kam_remake/src/ext/BGRABitmap/bgralazresource.pas
T
admin_d 7063d1b1c9
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s
sync: restore source from Google Drive
2026-09-12 16:50:30 +05:00

417 lines
11 KiB
ObjectPascal

// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit BGRALazResource;
{$mode objfpc}{$H+}
interface
uses
BGRAClasses, SysUtils, BGRAMultiFileType;
type
{ TLazResourceEntry }
TLazResourceEntry = class(TMultiFileEntry)
private
procedure Serialize(ADestination: TStream);
protected
FName: utf8string;
FValueType: utf8string;
FContent: TStream;
function GetName: utf8string; override;
procedure SetName(AValue: utf8string); override;
function GetExtension: utf8string; override;
function GetFileSize: int64; override;
public
constructor Create(AContainer: TMultiFileContainer; AName: utf8string; AValueType: utf8string; AContent: TStream);
destructor Destroy; override;
function CopyTo(ADestination: TStream): int64; override;
function GetStream: TStream; override;
end;
{ TFormDataEntry }
TFormDataEntry = class(TLazResourceEntry)
protected
FTextContent: TStream;
procedure RequireTextContent;
function GetExtension: utf8string; override;
function GetFileSize: int64; override;
public
constructor Create(AContainer: TMultiFileContainer; AName: utf8string; ABinaryContent: TStream);
destructor Destroy; override;
function CopyTo(ADestination: TStream): int64; override;
end;
{ TLazResourceContainer }
TLazResourceContainer = class(TMultiFileContainer)
protected
function CreateEntry(AName: utf8string; AExtension: utf8string; AContent: TStream): TMultiFileEntry; override;
public
procedure LoadFromStream(AStream: TStream); override;
procedure SaveToStream(ADestination: TStream); override;
end;
implementation
uses LResources, BGRAUTF8;
{ TFormDataEntry }
procedure TFormDataEntry.RequireTextContent;
begin
if FTextContent = nil then
begin
FTextContent:= TMemoryStream.Create;
FContent.Position:= 0;
LRSObjectBinaryToText(FContent, FTextContent);
end;
end;
function TFormDataEntry.GetExtension: utf8string;
begin
Result:= 'lfm';
end;
function TFormDataEntry.GetFileSize: int64;
begin
RequireTextContent;
Result:= FTextContent.Size;
end;
constructor TFormDataEntry.Create(AContainer: TMultiFileContainer;
AName: utf8string; ABinaryContent: TStream);
begin
inherited Create(AContainer,AName,'FORMDATA',ABinaryContent);
end;
destructor TFormDataEntry.Destroy;
begin
FreeAndNil(FTextContent);
inherited Destroy;
end;
function TFormDataEntry.CopyTo(ADestination: TStream): int64;
begin
RequireTextContent;
if FTextContent.Size = 0 then
result := 0
else
begin
FTextContent.Position:= 0;
result := ADestination.CopyFrom(FTextContent,FTextContent.Size);
end;
end;
{ TLazResourceEntry }
procedure TLazResourceEntry.Serialize(ADestination: TStream);
begin
FContent.Position := 0;
BinaryToLazarusResourceCode(FContent, ADestination, Name, FValueType);
end;
function TLazResourceEntry.GetName: utf8string;
begin
Result:= FName;
end;
procedure TLazResourceEntry.SetName(AValue: utf8string);
begin
if AValue = FName then exit;
if Container.IndexOf(AVAlue, Extension) <> -1 then
raise Exception.Create('Name is already used for this extension');
FName := AValue;
end;
function TLazResourceEntry.GetExtension: utf8string;
begin
Result:= FValueType;
end;
function TLazResourceEntry.GetFileSize: int64;
begin
Result:= FContent.Size;
end;
destructor TLazResourceEntry.Destroy;
begin
FreeAndNil(FContent);
inherited Destroy;
end;
constructor TLazResourceEntry.Create(AContainer: TMultiFileContainer; AName: utf8string; AValueType: utf8string;
AContent: TStream);
begin
inherited Create(AContainer);
FName := AName;
FValueType := UTF8UpperCase(AValueType);
FContent := AContent;
end;
function TLazResourceEntry.CopyTo(ADestination: TStream): int64;
begin
if FContent.Size = 0 then
result := 0
else
begin
FContent.Position:= 0;
result := ADestination.CopyFrom(FContent, FContent.Size);
end;
end;
function TLazResourceEntry.GetStream: TStream;
begin
Result:= FContent;
end;
{ TLazResourceContainer }
procedure TLazResourceContainer.LoadFromStream(AStream: TStream);
const
entryStart = 'LazarusResources.Add(';
entryEnd = ');';
whiteSpace = [' ',#9,#10,#13,#26];
var
fileContent: String;
filePos : integer;
procedure SkipWhitespace;
begin
while (filePos <= length(fileContent)) and (fileContent[filePos] in whiteSpace) do inc(filePos);
end;
procedure SkipComma;
begin
SkipWhitespace;
if (filePos <= length(fileContent)) and (fileContent[filePos] = ',') then
inc(filePos)
else
raise Exception.Create('Comma expected');
end;
function ParseString(ignoreCommas: boolean): TStream;
var
expectPlus: boolean;
procedure AppendChar(c: char);
begin
result.WriteByte(ord(c));
end;
function ParseNumber: integer;
var numberStart, errPos: integer;
s: String;
begin
numberStart:= filePos;
if (filePos <= length(fileContent)) and (fileContent[filePos] = '$') then
begin
inc(filePos);
while (filePos <= length(fileContent)) and (fileContent[filePos] in['0'..'9','a'..'f','A'..'F']) do inc(filePos);
end else
begin
while (filePos <= length(fileContent)) and (fileContent[filePos] in['0'..'9']) do inc(filePos);
end;
s := copy(fileContent,numberStart,filePos-numberStart);
val(s, result, errPos);
if errPos <> 0 then
raise exception.Create('Invalid number "' + s + '"');
end;
function ParseStringPart: boolean;
var charCode: integer;
begin
SkipWhitespace;
if filePos <= length(fileContent) then
begin
if expectPlus then
if fileContent[filePos] <> '+' then
begin
result := false;
expectPlus := false;
exit;
end else
inc(filePos);
case fileContent[filePos] of
'+': raise exception.Create('Unexpected "+"');
'''': begin
inc(filePos);
while (filePos <= length(fileContent)) do
begin
if fileContent[filePos] = '''' then
begin
inc(filePos);
if (filePos <= length(fileContent)) and (fileContent[filePos] = '''') then
begin
AppendChar('''');
inc(filePos);
end
else break;
end else
if fileContent[filePos] in[#10,#13] then
raise Exception.Create('Unexpected end of line')
else
begin
AppendChar(fileContent[filePos]);
inc(filePos);
end;
end;
if (filePos <= length(fileContent)) and (fileContent[filePos] = '#') then
expectPlus := false
else
expectPlus := true;
result := true;
end;
'#': begin
inc(filePos);
charCode := ParseNumber;
if (charCode < 0) or (charCode > 255) then
raise exception.Create('Character code out of bounds');
AppendChar(chr(charCode));
if (filePos <= length(fileContent)) and (fileContent[filePos] in['#','''']) then
expectPlus := false
else
expectPlus := true;
result := true;
end;
else
begin
result := false;
expectPlus := false;
end;
end;
end
else
begin
result := false;
expectPlus := false;
end;
end;
begin
result := TMemoryStream.Create;
expectPlus := false;
if not ParseStringPart then raise exception.Create('Expecting string');
repeat
if ignoreCommas then
begin
SkipWhitespace;
if (filePos <= length(fileContent)) and (fileContent[filePos] = ',') then
begin
inc(filePos);
expectPlus := false;
end;
end;
until not ParseStringPart;
end;
procedure ReadContent;
var
bytesRead: integer;
begin
setlength(fileContent,AStream.Size-AStream.Position);
bytesRead := AStream.Read(fileContent[1],length(fileContent));
setlength(fileContent, bytesRead);
filePos := 1;
end;
function StreamToUTF8String(AStream: TStream): utf8String;
begin
setlength(result, AStream.Size);
AStream.Position := 0;
AStream.Read(result[1], length(result));
AStream.Free;
end;
var
entryName: utf8string;
entryType: utf8string;
entryContent: TStream;
inArray: boolean;
begin
Clear;
ReadContent;
while filePos <= length(fileContent) do
begin
if (upcase(fileContent[filePos]) = upcase(entryStart[1])) and
(CompareText(copy(fileContent,filePos,length(entryStart)),entryStart)=0) then
begin
inc(filePos, length(entryStart));
entryName := StreamToUTF8String(ParseString(false));
SkipComma;
entryType := StreamToUTF8String(ParseString(false));
SkipComma;
SkipWhitespace;
if (filePos <= length(fileContent)) and (fileContent[filePos] = '[') then
begin
inArray := true;
inc(filePos);
end else
inArray := false;
entryContent := ParseString(inArray);
SkipWhitespace;
if inArray then
begin
if (filePos <= length(fileContent)) and (fileContent[filePos] = ']') then
inc(filePos)
else
raise exception.Create('Expecting "]"');
end;
if entryType = 'FORMDATA' then
AddEntry(TFormDataEntry.Create(self,entryName,entryContent))
else
AddEntry(TLazResourceEntry.Create(self,entryName,entryType,entryContent));
if (filePos+length(entryEnd)-1 <= length(fileContent)) and (CompareText(copy(fileContent,filePos,length(entryEnd)),entryEnd)=0) then
inc(filePos,length(entryEnd))
else
raise exception.Create('Expecting "'+entryEnd+'"');
end else
if fileContent[filePos] in whiteSpace then
inc(filePos)
else
raise exception.Create('Unexpected character "'+fileContent[filePos]+'"');
end;
end;
function TLazResourceContainer.CreateEntry(AName: utf8string; AExtension: utf8string;
AContent: TStream): TMultiFileEntry;
var
binContent: TMemoryStream;
begin
if UTF8CompareText(AExtension,'lfm')=0 then
begin
binContent := TMemoryStream.Create;
try
AContent.Position:= 0;
LRSObjectTextToBinary(AContent, binContent);
result := TFormDataEntry.Create(self,AName,binContent);
except
on ex:Exception do
begin
binContent.Free;
result := nil;
end;
end;
AContent.Free;
end
else
result := TLazResourceEntry.Create(self,AName,UTF8UpperCase(AExtension),AContent);
end;
procedure TLazResourceContainer.SaveToStream(ADestination: TStream);
var
i: Integer;
begin
for i := 0 to Count-1 do
TLazResourceEntry(Entry[i]).Serialize(ADestination);
end;
end.