// SPDX-License-Identifier: LGPL-3.0-linking-exception unit MacBundle; {$mode objfpc}{$H+} interface uses Classes, SysUtils, ReleaserTypes, laz2_XMLRead, laz2_XMLWrite, Laz2_DOM; type { TMacBundle } TMacBundle = class(TReleaserObject) private FBundlepath: string; FFilename: string; FXml: TXmlDocument; FDict: TStringList; FChanged: boolean; function GetName: string; function GetPListFilename: string; function ReadDict(ARoot: TDOMNode): TStringList; procedure UpdateDict(ARoot: TDOMNode; ADict: TStringList); function GetPList: TDOMNode; public constructor Create(AParameters: TStringList; ALogicDir: string); override; destructor Destroy; override; property Filename: string read FFilename; function GetVersion: TVersion; procedure GetVersions(AVersionList: TStringList); override; procedure CheckVersion(AVersion: TVersion); override; property Name: string read GetName; procedure Save; override; procedure UpdateVersion(AVersion: TVersion); override; end; implementation { TMacBundle } function TMacBundle.GetName: string; begin result := ''; if Assigned(FDict) then result := FDict.Values['CFBundleName']; if result = '' then result := ChangeFileExt(ExtractFileName(FBundlepath),''); end; function TMacBundle.GetPListFilename: string; begin result := FBundlepath+PathDelim+'Contents'+PathDelim+'Info.plist'; end; function TMacBundle.ReadDict(ARoot: TDOMNode): TStringList; var dict, entry: TDOMNode; key: string; begin result := TStringList.Create; if Assigned(ARoot) then begin dict := ARoot.FindNode('dict'); if Assigned(dict) then begin entry := dict.FirstChild; while entry <> nil do begin if entry.NodeName = 'key' then begin key := entry.TextContent; entry := entry.NextSibling; if (entry <> nil) and (entry.NodeName = 'string') then begin result.Values[key] := entry.TextContent; entry := entry.NextSibling; end else if (entry <> nil) and (entry.NodeName <> 'key') then entry := entry.NextSibling; end else entry := entry.NextSibling; end; end else raise exception.Create('"dict" node not found'); end; end; procedure TMacBundle.UpdateDict(ARoot: TDOMNode; ADict: TStringList); var dict, entry: TDOMNode; key: string; begin if Assigned(ARoot) then begin dict := ARoot.FindNode('dict'); if Assigned(dict) then begin entry := dict.FirstChild; while entry <> nil do begin if entry.NodeName = 'key' then begin key := entry.TextContent; entry := entry.NextSibling; if (entry <> nil) and (entry.NodeName = 'string') then begin entry.TextContent := ADict.Values[key]; entry := entry.NextSibling; end else if (entry <> nil) and (entry.NodeName <> 'key') then entry := entry.NextSibling; end else entry := entry.NextSibling; end; end else raise exception.Create('"dict" node not found'); end; end; function TMacBundle.GetPList: TDOMNode; begin result := FXml.FirstChild; while (result <> nil) and not (result is TDOMElement) do result := result.NextSibling; if (result = nil) or (result.NodeName <> 'plist') then raise exception.Create('"plist" node not found'); end; constructor TMacBundle.Create(AParameters: TStringList; ALogicDir: string); var stream: TFileStream; begin inherited Create(AParameters, ALogicDir); ExpectParamCount(1); FBundlepath:= ExpandFileName(ReplaceVariables(Param[0])); stream := TFileStream.Create(GetPListFilename, fmOpenRead); try ReadXMLFile(FXml,stream); finally stream.Free; end; FDict := ReadDict(GetPList); writeln('Bundle ',Name,' version ',VersionToStr(GetVersion)); end; destructor TMacBundle.Destroy; begin FXml.Free; inherited Destroy; end; function TMacBundle.GetVersion: TVersion; begin if Assigned(FDict) then result := StrToVersion(FDict.Values['CFBundleVersion']) else raise exception.Create('Version node not found'); end; procedure TMacBundle.GetVersions(AVersionList: TStringList); var ver: TVersion; verStr: String; begin ver := GetVersion; verStr := VersionToStr(ver); if AVersionList.IndexOf(verStr)=-1 then AVersionList.Add(verStr); end; procedure TMacBundle.CheckVersion(AVersion: TVersion); begin inherited CheckVersion(AVersion); if AVersion<>GetVersion then raise exception.Create('Inconsistent version of bundle '+Name); end; procedure TMacBundle.Save; begin if FChanged then begin writeln('Updating bundle ', Name,'...'); WriteXMLFile(FXml, GetPListFilename); end else writeln('Bundle unchanged'); end; procedure TMacBundle.UpdateVersion(AVersion: TVersion); var versionStr: String; begin if Assigned(FDict) then begin versionStr := VersionToStr(AVersion); if FDict.Values['CFBundleVersion'] <> versionStr then begin FDict.Values['CFBundleVersion'] := versionStr; FChanged := true; end; if FDict.Values['CFBundleShortVersionString'] <> versionStr then begin FDict.Values['CFBundleShortVersionString'] := versionStr; FChanged := true; end; UpdateDict(GetPList, FDict); end; end; end.