From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by mx.groups.io with SMTP id smtpd.web12.8338.1639646355753569788 for ; Thu, 16 Dec 2021 01:19:16 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=XF3YPDNu; spf=pass (domain: intel.com, ip: 134.134.136.65, mailfrom: yuwei.chen@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1639646355; x=1671182355; h=from:to:cc:subject:date:message-id:references: in-reply-to:content-transfer-encoding:mime-version; bh=O1rweKfNKpHP5qIm+mL7/qBg4XXxDcTqCetjTyaoa1I=; b=XF3YPDNutyuobpOxM4tE74LqU0kes1zNjwHNzdUxfPZGpeuSiBf+WuWW YKYwXdgYbdB6YR8baCtPedh9SjnJMYAPzL5xWOY2Nw2KAGezwoUuZzSBM b0sJJ8kTUlXfHgWAkHS3eo08DE4o9SIy3DxvhRPJtVV4wCY+VmnIcBLGg SIwV3xdukGt9uRHUy5oWyjniAyvOAAdT/y9XstlgXFUP/22IP6aI72bCQ 4B7OIGPu0ki/X5In24C4u+jTEV4AM8A7orsn9OWs1XP5yxMwC+I38qg0G yZzzl+OgqwxT1UKEWjgYEpxknoKe4B/C0135vP7giJFy849cxCPqHbukY Q==; X-IronPort-AV: E=McAfee;i="6200,9189,10199"; a="239397352" X-IronPort-AV: E=Sophos;i="5.88,211,1635231600"; d="scan'208";a="239397352" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by orsmga103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Dec 2021 01:19:14 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.88,211,1635231600"; d="scan'208";a="465970829" Received: from fmsmsx603.amr.corp.intel.com ([10.18.126.83]) by orsmga006.jf.intel.com with ESMTP; 16 Dec 2021 01:19:14 -0800 Received: from fmsmsx606.amr.corp.intel.com (10.18.126.86) by fmsmsx603.amr.corp.intel.com (10.18.126.83) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Thu, 16 Dec 2021 01:19:13 -0800 Received: from FMSEDG603.ED.cps.intel.com (10.1.192.133) by fmsmsx606.amr.corp.intel.com (10.18.126.86) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20 via Frontend Transport; Thu, 16 Dec 2021 01:19:13 -0800 Received: from NAM02-BN1-obe.outbound.protection.outlook.com (104.47.51.44) by edgegateway.intel.com (192.55.55.68) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2308.20; Thu, 16 Dec 2021 01:19:12 -0800 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=MdTaQioApCshWa8KSnSGesHt31aNtm0cmWfnA9YxYAswzRJZQmKqMrGkPuWjjz2lZdy8JmN37Nnm343qYtb7fYSr72+HACQGLpSwAwXE/7kmyeJsqydSTzk5OO/KNIDEnNNDFX1GvIrJOFDuf5pbpknrKe/VDSIVEl2ElVbi01BCaTeEaAEpj/Hv34MHnrQjGZLBllMiCAETRdduYyKRNYv5cYpYKNZXp2SpgHbAtirtpPFWV4uBUccf4vt+x3jrLZHqNdnopG546f5AUvDCawixuWJFoevJzzh/KteXoRkLU/4wLnu9ZrvixB2EMCfhLUr0lz/arctjRV9wZuNbfA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=wrRQpw0W71OfrPTN4+V8ZrTEkJgc3vXrmy7iaE+BdFQ=; b=ldW4glCHSNUyB6QzI/ax/jmJlea//7Jar2I5B0/8U5WW2qAcjMfjXH6LavHNekG7SPY1kZGNimcLtJ0BHH/cxwQudq62lowinDTM+ACuSgpkgl8kMH+fJZGXpQkCK+RTVqfmFxzQNd4NLZpNChqHncivcC5I7S4tMreVLee3nhv/bjvqQ1ry8DPWaFLF15m0WtsR/30aSdeMjW92aEtcKP90g+LiWI3ODvqzmT+nHPB6IykM3JcxnjIoPi6pFj2/E/u3TvCPH2iBnvrZAe412RSj8SS9yNpKJYiNbXtbpDmuZWsgG5YnTRzuPeyHfKB7+PUjqNDuehjTWAgf81ZSRw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Received: from MW5PR11MB5906.namprd11.prod.outlook.com (2603:10b6:303:1a0::21) by MW3PR11MB4665.namprd11.prod.outlook.com (2603:10b6:303:5d::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4778.17; Thu, 16 Dec 2021 09:19:01 +0000 Received: from MW5PR11MB5906.namprd11.prod.outlook.com ([fe80::8113:9bd8:1935:e5d4]) by MW5PR11MB5906.namprd11.prod.outlook.com ([fe80::8113:9bd8:1935:e5d4%4]) with mapi id 15.20.4778.017; Thu, 16 Dec 2021 09:19:01 +0000 From: "Yuwei Chen" To: "devel@edk2.groups.io" , "Gao, Liming" CC: "Feng, Bob C" , "Chen, Christine" Subject: Re: [edk2-devel] [Patch V5 1/1] BaseTools: Add FMMT Tool Thread-Topic: [edk2-devel] [Patch V5 1/1] BaseTools: Add FMMT Tool Thread-Index: AQHX8l22eWZPQ9Z4Wk+HF97uSAjHNaw01sHA Date: Thu, 16 Dec 2021 09:19:01 +0000 Message-ID: References: <16C1318B011F9705.32027@groups.io> In-Reply-To: <16C1318B011F9705.32027@groups.io> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-version: 11.6.200.16 dlp-reaction: no-action dlp-product: dlpe-windows authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: be46fc4b-ba0b-4ca6-1c8e-08d9c075192b x-ms-traffictypediagnostic: MW3PR11MB4665:EE_ x-ld-processed: 46c98d88-e344-4ed4-8496-4ed7712e255d,ExtAddr x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:4125; x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: o9+uB46MXkEZwbjrH7pbErW/qh1EZc7+5lb0MCETWJiNZDSkUwIL5T5YKhaeEliavTmsT6MsYwbpy9f6yM10kPizLxgqqXTDBf+YsMznSAFtFqP/XTId1mK7j1TB7swkjN1hM/pNQwm1ZYh3JY5NWGsEypd27GvwK9B5gpZ9hx4onRQlgOFeT+ZfrqDnM5RQ/cEUOGkVcZEh6OEtjM4TDyQeG4je7WVPVHk//pdrmaYfZh4NmwFy9FfxAr2L64HjlBtbxEWU+6GIKnTSgm3x7HuJVi+SAoE8bGbkOs+FHoqiQUDFEVWZDdhnjET069wUCfEWnCeVVb/KTQBVchdMufQSNP0Irfyb/75HLkRbI+Ge3UCUgqs3ntIgOSsL8iC7Vg+qqy7kF4fQGHHCQL4NvJMRSdn02M/cosQxrVAU6eu+Uw7YgMG4HOm+wOgPKezuo8PqXEgLQbXx6mO68J5yYXFqKAETAMMmf7z2z5zAoEzcmQhSujEITIyC698luk3nuYDULUjWhf5JpQplitihJeHaeucgMNVQx3QEwwc08SRYD6y5C1i87ULPbwjnk524iOmfTxHkXRqwmFzh24lCmZClpf+0NxdeykUlT83zHcSb3c97vnfdd7o94Bgw2z5kyN8gkJxU5HuDrUk4NSPkgKrvaeIjNF3kgZVG1po1Mqo0lrs0Hz3YLpsQtrV84GypOTTdPtQhT9c5p2729qc24tjCt3W6KAmwT6SiK1pZkDzcb2dt8Qw3ztXasjEOAf3Lz7inX7IdKakgVDiCPq2lND7jKRLMiyH3OCC4CBCztgL+P4bnQQLAxnlQyaOtx2km+jP3b0ZaLA/FQPT+tIlY4409XVeOzqN/+gJqxCwpAw22Xv+CLqIfdfLfKl9bOuqV x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:MW5PR11MB5906.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(366004)(38100700002)(316002)(71200400001)(5660300002)(8936002)(64756008)(30864003)(107886003)(186003)(9686003)(66946007)(7696005)(66556008)(2906002)(508600001)(76116006)(19627235002)(4326008)(38070700005)(122000001)(66476007)(8676002)(83380400001)(82960400001)(86362001)(66446008)(33656002)(52536014)(54906003)(110136005)(6506007)(26005)(55016003)(53546011)(966005)(579004)(559001)(357404004)(134885004);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?au3x2hqbzA/hWJ+XCSNOjoc6QhmVXSw7gNTxxIl2cruh6jPlDOdN6Okamfci?= =?us-ascii?Q?TPMykdDUm+k0UqrtaH4Hm+T0LArXKIifDGMYhyyfm8Bt9l5jPLZXqKwXdfI9?= =?us-ascii?Q?Ya3M+XLr3DC4RhlolYGAqcLm2a/huDvAt2o42aoqAb8nSmCdTxjWaPkFfQS8?= =?us-ascii?Q?8BKtPPUmibwqVmjpjpx3VrJarM5+2RRsQTvslhcq8oMyd4Di3FlyiRU1LfvD?= =?us-ascii?Q?Zj4GL5imtRiv/mVZceXwOqUKMg8E7rEqjBe7z5yEtuw6ad+jXDPWhd+71E6f?= =?us-ascii?Q?woUnkgqS2/fcGoIO/48rNI/yRQP3VmJQMN69Da3/L2BDm8ARLron5+ybkl+X?= =?us-ascii?Q?iIQxTEWyCKJBIUlbAyvcMLlP6Mf3aiexr0FAUUnBCkdlD4UrfxpLa0l4MbH3?= =?us-ascii?Q?M9mSEheNTaQjobk9jCOpWbgSFjivLaJVxejAch5w5pldYsKKNi4j9nszrP8J?= =?us-ascii?Q?LH2UyfKcp0184L08ui8o0CZaN7Yg5VaG28mIK50pcgjiLxoc6gu+k5uwlEoi?= =?us-ascii?Q?UQWOzZ6uDTVYAKZ/POkGZbeporp1BvcxlGd3e+HuoFNgE2D8jBTdfawpA7tA?= =?us-ascii?Q?jFoBP8te7wufl/NoolBFHjCZVvejUW5CJiNKcSugBS24os57TP4nZrGD+GjP?= =?us-ascii?Q?dsXivWBVe4ls8Ijfy9/b07G2ZYgv65a87GPJQ3rBqatA5tpj+UzJBAttbE+5?= =?us-ascii?Q?VOtYnKmQOKnTDSYB2Kduup1MNi5lR5dLHE8Loo9SIozLZNhfaWDcp4un2wVN?= =?us-ascii?Q?mKG35lIxHPdfLNcEBiZYLxODHb2tlQgu7UH/LLQEVkwH1PUHcGcDBR7ig/85?= =?us-ascii?Q?Dv/s6JkhjwQBJZPfpT6UnkIivVan/wDH0JDjVTeKlT6yq/LZOCTh1EaLzFjp?= =?us-ascii?Q?55Q1w2zdoLUK9H/VM3kxzPMXsf54AtlfPNOIuVgmCJxq2Oup9+RUEZouaIx9?= =?us-ascii?Q?9GZFNcEueYt4dawOqCTkfom33A0ZXi5nzBrtJ6rt++NxtKBpKak+6pwcmtGH?= =?us-ascii?Q?sGCXnNHJWiYlnhwyveIkWczN5xRcxgT1CLMp2Otl9tM9d6Wpn6l/Ptid3QmV?= =?us-ascii?Q?KGgcFJiXvZvcEcOAvBb4/dfE9dBE+jmvDRn0NCrVWI4ym1O2wcXZrxckSg5o?= =?us-ascii?Q?Ais4K+vpQgN211a+W6OW/PgnGqQ4e2bWFExb4U4rS+UhkHfJBD5t+iP1GbIl?= =?us-ascii?Q?GYYOoJI6q3XiA9FVgT36I0yznLt0akcfBbsHvvhz2eI0+flnbofM9SyYiPTu?= =?us-ascii?Q?B8r9E1Dwo6baI0IG2gXbiwXbqqU07YtutY4c9NZl4qZs+yD/4HCFjCiLjvx9?= =?us-ascii?Q?KhTTUlPw+E7MGL9QxbgHT948uuYkE2fMKShFGuF7hpC1QtdQMv8hmnyDwziO?= =?us-ascii?Q?eHN7mD4rp/nyUaxtc+w8wRCuYtAl682p6Yn9563YFlS3fXFtWwvH7hQ7PHRC?= =?us-ascii?Q?oDTg3xLxDPvIe7FEAOGXbMDfs1G/ne7zXTGSsDxsjG4t8CIGrAfhxdtX5GOt?= =?us-ascii?Q?vodmV6czTDjIYGN8IsJto708Kgxrn/Bg2ba2/6vcSgFFmwmEuInNY98IgK2b?= =?us-ascii?Q?HUhCu+VvjINAogKGrmL0X9f5p0smX+ZYEKaKgHzu02NwORGceq4PtQumz/Zz?= =?us-ascii?Q?7A=3D=3D?= MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: MW5PR11MB5906.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: be46fc4b-ba0b-4ca6-1c8e-08d9c075192b X-MS-Exchange-CrossTenant-originalarrivaltime: 16 Dec 2021 09:19:01.3723 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: W6Sexv8N+LpV4+u3zg0AicLmzmN8LW7KTf97aI5My97vsFhd34AxUlSVtav6JgicbN7xh9Gf1yOWypS80m7k0Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MW3PR11MB4665 Return-Path: yuwei.chen@intel.com X-OriginatorOrg: intel.com Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Hi Liming, This patch has fixed the first two issues (Non-ZeroVector Header & Delete f= unction bug) you mentioned in last email. For the third one (Rebase feature), It is related to PE32 & COFF. Thus, we = plan to save the current FMMT python to Stage1, and take adding PE32 & COFF= definition and related rebase feature as stage2. Could we push the stage1 to BaseTools firstly, then modify the stage2 chang= es on this basis? Looking forward to your inputs. Thanks, Christine(Yuwei) > -----Original Message----- > From: devel@edk2.groups.io On Behalf Of Yuwei > Chen > Sent: Thursday, December 16, 2021 5:17 PM > To: devel@edk2.groups.io > Cc: Feng, Bob C ; Gao, Liming > > Subject: [edk2-devel] [Patch V5 1/1] BaseTools: Add FMMT Tool >=20 > The FMMT python tool is used for firmware files operation, which has > the Fv/FFs-based 'View'&'Add'&'Delete'&'Replace' operation function: >=20 > 1.Parse a FD(Firmware Device) / FV(Firmware Volume) / FFS(Firmware Files) > 2.Add a new FFS into a FV file (both included in a FD file or not) > 3.Replace an FFS in a FV file with a new FFS file > 4.Delete an FFS in a FV file (both included in a FD file or not) > 5.Extract the FFS from a FV file (both included in a FD file or not) >=20 > Currently the FMMT C tool is saved in edk2-staging repo, but its > quality and coding style can't meet the Edk2 quality, which is hard to > maintain (Hard/Duplicate Code; Regression bugs; Restrict usage). >=20 > The new Python version keeps same functions with origin C version. It > has higher quality and better coding style, and it is much easier to > extend new functions and to maintain. >=20 > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D1847 > PR Link: https://github.com/tianocore/edk2-basetools/pull/12 > RFC Link: https://edk2.groups.io/g/devel/message/82877 > Staging Link: https://github.com/tianocore/edk2-staging/tree/PyFMMT >=20 > Cc: Bob Feng > Cc: Liming Gao > Signed-off-by: Yuwei Chen > --- > V5 fixed the FvHeader parse and delete ffs issue. > BaseTools/BinWrappers/PosixLike/FMMT | 14 ++++++++= ++++++ > BaseTools/BinWrappers/WindowsLike/FMMT.bat | 4 ++++ > BaseTools/Source/Python/FMMT/FMMT.py | 117 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++++++++++++++++++++++++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/FMMTConfig.ini | 11 > +++++++++++ > BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png | Bin 0 -> > 29515 bytes > BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png | Bin 0 -> > 79906 bytes > BaseTools/Source/Python/FMMT/PI/Common.py | 81 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++++++++++ > BaseTools/Source/Python/FMMT/PI/FfsFileHeader.py | 66 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++ > BaseTools/Source/Python/FMMT/PI/FvHeader.py | 112 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/PI/SectionHeader.py | 110 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/PI/__init__.py | 6 ++++++ > BaseTools/Source/Python/FMMT/README.md | 180 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/__init__.py | 6 ++++++ > BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py | 371 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++++++++++++++++++++++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/core/BiosTree.py | 198 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++ > BaseTools/Source/Python/FMMT/core/BiosTreeNode.py | 191 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/core/FMMTOperation.py | 140 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++ > BaseTools/Source/Python/FMMT/core/FMMTParser.py | 86 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/core/FvHandler.py | 521 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++ > BaseTools/Source/Python/FMMT/core/GuidTools.py | 152 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++ > BaseTools/Source/Python/FMMT/utils/FmmtLogger.py | 18 > ++++++++++++++++++ > BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py | 54 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 22 files changed, 2438 insertions(+) >=20 > diff --git a/BaseTools/BinWrappers/PosixLike/FMMT > b/BaseTools/BinWrappers/PosixLike/FMMT > new file mode 100644 > index 000000000000..c90a770f2e00 > --- /dev/null > +++ b/BaseTools/BinWrappers/PosixLike/FMMT > @@ -0,0 +1,14 @@ > +#!/usr/bin/env bash > +#python `dirname $0`/RunToolFromSource.py `basename $0` $* > + > +# If a ${PYTHON_COMMAND} command is available, use it in preference to > python > +if command -v ${PYTHON_COMMAND} >/dev/null 2>&1; then > + python_exe=3D${PYTHON_COMMAND} > +fi > + > +full_cmd=3D${BASH_SOURCE:-$0} # see > http://mywiki.wooledge.org/BashFAQ/028 for a discussion of why $0 is not = a > good choice here > +dir=3D$(dirname "$full_cmd") > +cmd=3D${full_cmd##*/} > + > +export > PYTHONPATH=3D"$dir/../../Source/Python/FMMT:$dir/../../Source/Python${PY > THONPATH:+:"$PYTHONPATH"}" > +exec "${python_exe:-python}" -m $cmd.$cmd "$@" > diff --git a/BaseTools/BinWrappers/WindowsLike/FMMT.bat > b/BaseTools/BinWrappers/WindowsLike/FMMT.bat > new file mode 100644 > index 000000000000..f0551c4ac54a > --- /dev/null > +++ b/BaseTools/BinWrappers/WindowsLike/FMMT.bat > @@ -0,0 +1,4 @@ > +@setlocal > +@set ToolName=3D%~n0% > +@set > PYTHONPATH=3D%PYTHONPATH%;%BASE_TOOLS_PATH%\Source\Python;%BA > SE_TOOLS_PATH%\Source\Python\FMMT > +@%PYTHON_COMMAND% -m %ToolName%.%ToolName% %* > diff --git a/BaseTools/Source/Python/FMMT/FMMT.py > b/BaseTools/Source/Python/FMMT/FMMT.py > new file mode 100644 > index 000000000000..5028d8446ee0 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/FMMT.py > @@ -0,0 +1,117 @@ > +# @file > +# Firmware Module Management Tool. > +# > +# Copyright (c) 2021, Intel Corporation. All rights reserved.
> +# > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +# > +## > + > +# Import Modules > +# > +import argparse > +import sys > +from core.FMMTOperation import * > + > +parser =3D argparse.ArgumentParser(description=3D''' > +View the Binary Structure of FD/FV/Ffs/Section, and > Delete/Extract/Add/Replace a Ffs from/into a FV. > +''') > +parser.add_argument("--version", action=3D"version", version=3D'%(prog)s > Version 1.0', > + help=3D"Print debug information.") > +parser.add_argument("-v", "--View", dest=3D"View", nargs=3D'+', > + help=3D"View each FV and the named files within each= FV: '-v > inputfile outputfile, inputfiletype(.Fd/.Fv/.ffs/.sec)'") > +parser.add_argument("-d", "--Delete", dest=3D"Delete", nargs=3D'+', > + help=3D"Delete a Ffs from FV: '-d inputfile TargetFf= sName > outputfile TargetFvName(Optional,\ > + If not given, wil delete all the existed target Ffs)= '") > +parser.add_argument("-e", "--Extract", dest=3D"Extract", nargs=3D'+', > + help=3D"Extract a Ffs Info: '-e inputfile TargetFfsN= ame outputfile'") > +parser.add_argument("-a", "--Add", dest=3D"Add", nargs=3D'+', > + help=3D"Add a Ffs into a FV:'-a inputfile TargetFvNa= me newffsfile > outputfile'") > +parser.add_argument("-r", "--Replace", dest=3D"Replace", nargs=3D'+', > + help=3D"Replace a Ffs in a FV: '-r inputfile TargetF= fsName newffsfile > outputfile TargetFvName(Optional,\ > + If not given, wil replace all the existed target= Ffs with new Ffs > file)'") > +parser.add_argument("-l", "--LogFileType", dest=3D"LogFileType", nargs= =3D'+', > + help=3D"The format of log file which saves Binary la= yout. Currently > supports: json, txt. More formats will be added in the future") > + > +def print_banner(): > + print("") > + > +class FMMT(): > + def __init__(self) -> None: > + self.firmware_packet =3D {} > + > + def CheckFfsName(self, FfsName:str) -> str: > + try: > + return uuid.UUID(FfsName) > + except: > + return FfsName > + > + def View(self, inputfile: str, logfiletype: str=3DNone, outputfile: = str=3DNone) -> > None: > + # ParserFile(inputfile, ROOT_TYPE, logfile, outputfile) > + filetype =3D os.path.splitext(inputfile)[1].lower() > + if filetype =3D=3D '.fd': > + ROOT_TYPE =3D ROOT_TREE > + elif filetype =3D=3D '.fv': > + ROOT_TYPE =3D ROOT_FV_TREE > + elif filetype =3D=3D '.ffs': > + ROOT_TYPE =3D ROOT_FFS_TREE > + elif filetype =3D=3D '.sec': > + ROOT_TYPE =3D ROOT_SECTION_TREE > + else: > + ROOT_TYPE =3D ROOT_TREE > + ParserFile(inputfile, ROOT_TYPE, logfiletype, outputfile) > + > + def Delete(self, inputfile: str, TargetFfs_name: str, outputfile: st= r, > Fv_name: str=3DNone) -> None: > + if Fv_name: > + DeleteFfs(inputfile, self.CheckFfsName(TargetFfs_name), outp= utfile, > uuid.UUID(Fv_name)) > + else: > + DeleteFfs(inputfile, self.CheckFfsName(TargetFfs_name), outp= utfile) > + > + def Extract(self, inputfile: str, Ffs_name: str, outputfile: str) ->= None: > + ExtractFfs(inputfile, self.CheckFfsName(Ffs_name), outputfile) > + > + def Add(self, inputfile: str, Fv_name: str, newffsfile: str, outputf= ile: str) -> > None: > + AddNewFfs(inputfile, self.CheckFfsName(Fv_name), newffsfile, > outputfile) > + > + def Replace(self,inputfile: str, Ffs_name: str, newffsfile: str, out= putfile: str, > Fv_name: str=3DNone) -> None: > + if Fv_name: > + ReplaceFfs(inputfile, self.CheckFfsName(Ffs_name), newffsfil= e, > outputfile, uuid.UUID(Fv_name)) > + else: > + ReplaceFfs(inputfile, self.CheckFfsName(Ffs_name), newffsfil= e, > outputfile) > + > + > +def main(): > + args=3Dparser.parse_args() > + status=3D0 > + > + try: > + fmmt=3DFMMT() > + if args.View: > + if args.LogFileType: > + fmmt.View(args.View[0], args.LogFileType[0]) > + else: > + fmmt.View(args.View[0]) > + if args.Delete: > + if len(args.Delete) =3D=3D 4: > + > fmmt.Delete(args.Delete[0],args.Delete[1],args.Delete[2],args.Delete[3]) > + else: > + fmmt.Delete(args.Delete[0],args.Delete[1],args.Delete[2]= ) > + if args.Extract: > + fmmt.Extract(args.Extract[0],args.Extract[1],args.Extract[2]= ) > + if args.Add: > + fmmt.Add(args.Add[0],args.Add[1],args.Add[2],args.Add[3]) > + if args.Replace: > + if len(args.Replace) =3D=3D 5: > + > fmmt.Replace(args.Replace[0],args.Replace[1],args.Replace[2],args.Replace= [3 > ],args.Replace[4]) > + else: > + > fmmt.Replace(args.Replace[0],args.Replace[1],args.Replace[2],args.Replace= [3 > ]) > + # TODO: > + '''Do the main work''' > + except Exception as e: > + print(e) > + > + return status > + > + > +if __name__ =3D=3D "__main__": > + exit(main()) > diff --git a/BaseTools/Source/Python/FMMT/FMMTConfig.ini > b/BaseTools/Source/Python/FMMT/FMMTConfig.ini > new file mode 100644 > index 000000000000..aa2444f11b38 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/FMMTConfig.ini > @@ -0,0 +1,11 @@ > +## @file > +# This file is used to define the FMMT dependent external tool guid. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +a31280ad-481e-41b6-95e8-127f4c984779 TIANO TianoCompress > +ee4e5898-3914-4259-9d6e-dc7bd79403cf LZMA LzmaCompress > +fc1bcdb0-7d31-49aa-936a-a4600d9dd083 CRC32 GenCrc32 > +d42ae6bd-1352-4bfb-909a-ca72a6eae889 LZMAF86 LzmaF86Compress > +3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI BrotliCompress > diff --git > a/BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png > b/BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png > new file mode 100644 > index > 0000000000000000000000000000000000000000..7cc806a0133413a4aa037f1 > d43d80000546a5246 > GIT binary patch > literal 29515 > zcmcG$bySpZzxIs{h;*kQ-QA%B(hbs~fb@{kr2^7}lF~VJNaui3BP|_6cXz|UdyT(+ > z@4cV*xu0jfYp?bGHx8Fqo%KDA<8w}!x~kkWOma*#G_+?5@- > mueX!oILX!ky#KL)-b > z*L=3Dx mKL > z!H*H;#x^;-U0hqXV6&1jfgcuHHo6(wtPm9?{5-*01vG}q*T?cd3(SjgooeVt#2I(K > zsH ~0Os > zomQa`7xm?Vn1W*P-%s- > jnykPv=3D(IPgF`B5O(HA{p;xPYyYK_g+{^ty7^w7WOYTKu0 > zA^Q8tpM+meOs7ygu*QzofkppB890#CH!vaY^9>J4dxmd4K5&@Bvj#t||Iyi*{) > abe > z{I&G~u33SY%Zhw!or;12*-v@k`k_A=3DB>uMt_@7?l- > @J?eZ>M^7i~SOv=3DZ7|He5*9B > z*-Jfk;((ApOh d`(_=3D5+Rb$5YAn{Zni!r4Nv_nO&2?akG)bWu0KC~_e > z5hX~!dqa>0Ni%KxN)V+;B4>4tz91qe)gfE4g(?YCU- > u*mWMw) > zYZ9ndV3HB4SQ8nO$7M7)G~o%W(JEAbO)~r^m;%8wqGZk66tMp^SusJR)1OK > !@Kal=3D > zFkG_T1(S#Wdumz4sds}>;2sSjqjb6%<#>h~^ 1 > z=3D$FJ1uc?nn$D W > zXqH{ML)x#35z}x<`aV8yk7N|DPc;Gx-6CAy- > 4#Vx^C$n*R(S2# zbJ!y_4}?SBuis15Cei)O^LmHVr^*^U&I<- > BdBP zphoUDj_aEpvqlQD(uilrobt-C5{L_ > zKY7868Ua|J(c~4MfY3*(TH(nB2GP0P+Bxq&DSbY9)L*l2HfpW6U@eef YoAv$ > zg7tl~Kq^RbPmZvpAuX0fvSRm`l_t&lIp?YgqiAhS4b?SRXPx6j{^K=3Dy`Tl5-b>&o% > z#9}QDAC=3Dn^W?SEn@tay29_cH(Qj)(%kM8=3D oZlL > z&3gKW-6^Vf#y668S!jqIoodn?gSJCO*!zSzK0f{yTRQaPa|Ii&t~@D~gfrf#(ptk` > zYfncLj2YRs5i(*1g&0ZS zr)BCKG}+9b(5C=3D0v{@*{xmzDkP4#BoWTW|6Z$l#hS$w!Vs;do3fG{lPY}($uXtKr > k > z^7OAq1SAXvaSeUzY`WRIfIpy^nC!H8-`_boX- > Sd(_WJqP_|B0b^CxF|AoL+M;CAVm > zm}1k@2fAaKkW>)db`vyuD929kh1kSlkjP_YnBzunpzF22s)kafVH^5GmG#>`rxT > +5 > z&7X7TiJ+^`+!Myi&54A&3`MFnX0=3DRZo37~5p^Rmf5I!1@U+{pnP@Wi;SXnWu > D}JIY > zr^ z+fwVl+Vm*}=3DB(e9w+RoY;I;K#(>zxV2))8~J%94)=3DGk > znUnL(gA^3WY*ErV59QLuHV- > =3DJ0m0eEDjs15%NH+H9hW653 > zz3kXsjNvzi(X;sY`z}o%w$Lr}Rcg=3Dn+-ircv6uwuNZn5E^VY~hGQj!E_7ZYc312rz > z)x6VsL$EfMsh^8nVXMOrI<8jVs&!paI!bj-d|`jvIbK+si;&!T2_F_&_HNh zu?$hyn4d$wdVWZ~+NEf&=3D!2BP6~=3Dn}fQ*c+!~|yG@B- > 8MDTh)0*RC#EN#u!^nVH$m > zzgh)3l-4|JU2GA^h^aCxz6Mv-Zhmg- > ^Z7&bz___k1a7p>xHB|v!ubX%tr}+euxdm$ > zr@UZ!Cb*Y9RjAa*@Ysvhor3#K*>m?e%y?Tu4~{uV_j?!Z`Q_advBucFI}vz_7^|1 > l > zs#*3J+AHPINX8;)QiLwy6-Z2S$&-jZ_3GKYZG z+bz!N8P0v$7bmTDT!M~sI~P|J^A}%eUf`LngoTB@ySX_0J0`f7?s|%)M As > zD_nFX!e0mNU<}uiOlgt9o5>Vmp|#FX4#&z}Q`3+O7G=3D%A7y6~w0#YL$- > *{a=3Dn?K!V > zzqL5yH*wusZE9O|6aMn_gl(?HB=3D9@5WKgu9#{ZC_C(_{7zuN*H5Scl7- > 918Sb}rWM > zKu}~4YZm`$e$42- > HyX^*ARyqnSUwP&U1IL265jq`r~Al#d|yN<*_D@z5<)EzK4ksy > z?cq{q2ONGaLI+Gi*#WY?nwXi`0YMy{qT~|}?{ou)>0cOr*4Aq00ps&Ad2f=3DWdU > CN$ > z3)pHvd?nrquvYW5RI&wU>uQgKG > |Lx3X6 > zHQK_7&MSD(#jg`*Qr#e=3D8|%+2t+Bz$`Ym)E;_}tQgP}S19bEo(JV9H#sgqdVfhktP > z4}!3MY9t1~*EvD4lR~M)=3D-uo73Np^_<_#SkowbWApZV)_)SI`kc66lik4?_aJ69P! > zdAyOE<@D`H@a*zwt=3D*1^oMD5zN5C?=3DZ%o{M?&Jgbgl4MGx0=3Dgxb?_0pb8b > 3+@WmhX > z>ZGUkb+ts3Tk&n$W- > n50EmVPV4rbP{sYhnPWD2CQkvs@ > zj)=3D>LWtfOq<($18M;{J)eP!RJ8w@ikz)-!o_k?1CJInjgyPOhp1A%MDgUMftCS$J~ > zs~z=3DIeuEWX?~IM;x16IPm|$IzVscrUQA- > 1pvp;SGObla#%wB7pN+^fnJc#a?jT)B8 > zr)Khe^ksZp$9->R)AM#A5a)`Aoc6w{mKMSJe1MeeS|6EzSmEf&- > l~CT@#e0@&vmX7 > zJ_wp%vzi$5r<=3D7kljM~V`|f>zT5Y+)*XvX2RACO$-}Y*-7<1UbT|>O|@l;?iY=3D`N4 > z(9OA%WUAZzNO!6Ai;3l$oSs)rly;5Lhg-}wddzR;%=3DbruKV8JvIpR~ss`}fAUYg*% > zg_$H!-&YS|{8p9oIhAV8;2~ZS;`RCFob(pNdtZux21525n^Jh;!h3xnEs~6{D|!eW > z@1oXWwkPBEgbB^o&Q5N@iOeu+2OO=3Db F zU9bQwqC@~@4%#T&M2@~I;`IHTutq0e!@A>BqQ6+lbopLu|0YyVZy` > TJt!CY > zZezN}4wz=3D94~>e){&{8Z- > !pi_<_>Q*^E1xO`K8rfPMi?eMFO=3DZmh^t}F=3DTnWq=3DcPC > z?nTG*M)zIv%gfoipFO?3A&Zedg`2Z?$#a6#(w*KET)n(@4| > bv > zC*D6&CiG{W)gW~uD@nWSO)bzKhXyC2EyTe*(564ZbA8OeQUB}9`FBlP+00lb > HJ6Kn > zcW5oArH&O&iy$dE`i=3D$R|12WEk|rl78- > 3{^BqoMr9wIa>5?kd89e6FZh*HvTIrq;E > zMtpFH;58k N5=3D1 > z{ht*Uo&~?TA}&7|Fx{896v5!>6Xvq2cbFBGlBJVbKs34)FK@4>`#3yQb*PL S > zn)`wKJJt$w)YDDdOz!E;Q^_%wW1WoBr?IqyAqFljl!Lu3+N^CZCV54L>qBBo ?; > zS=3Drg2r>6}ntEx~-?7dz;mytY`lpE5hI|UU-o=3DJL^Q!$$DxKo`Bj-roO zND(q|Lzk{yf^06hp@J7ecXs}I=3DC_XGpECbg#?|wd`p#;D(wSNkGu%gAD|w#;hw > Z%2 > z(x3bTp!XGe>@cBiIOGlMW19)- > jP94JlcGPOz|I@CnNHq}U5WPPK6dNBl;utt > z^X^6_m~Xoocok!;eVHcC9`CkU=3D)In7wB8yNVM>^2u- > 9w%D+WylIItDc+R{L*5W0`! > z>WG@K7WQnJExgUBC@9XqZS(w;a4OVhwY9bNcHHQE8D+N}frp$UxYs_v59{ > es93C0D > zx4n`V_yp%nG(mgLhYt7R7{(+4Z3hKxRYfuS`WI > 3g{x5 > z9vUc{Wq4aFGhTb~XTVR~wsouO9sylgL}Sq5Qo#AH=3Dbq@`kNtdT^- > 1kaKB+d7TDrx2 > ziyvyS!K0OwOa~Ux+Y7i9WD+>pjBQLDHhFMGis_gZ?y9s{qUigUajT}VdhxC7UB( > wX > zwR^dzsvK7e+XeI2%3Ml~VZVk)M>|qQTz_|WFK%dX^^RcDKGs%Nj*NUkv_u?3 > XkOqt > zYGwFphna|VjCs$XnJTt0Cj9Cwy$ DU > z)8b)fR7>yMnX0;XftbHtTen83qfbl z64j(LzHu=3DK+*tR0=3Dh4>HJqCtrkLWoFE5>f3Y- > oCVYo9xaw1S?6YWT`9V^=3Dh zHAIHMg- > ^ zI)!q*`yHlJrnRe?hu)X>7%Nz0cDuQ06n17ZI1NqJ(0vd*#G3t-!imMfLU8CUGd)w^ > zoFpU_^xzr9Mdb`B17|9g;{;s!TJ7+>7nqPU9!!bn%9*yE;Wp0i8~^P5WJ > L > zVyY{f1k_hWV%z)LX%{wzr&}aK4NM}ZP1IX)#tvsNNyA zbu=3DzgTiJy#ynK5jRs@N^w(fE>b}I$r-rE>0)ItNAt84JvNm_%AR4>7K` z>*bd>3NHK ?%k;$V^R5XclbB!ZjD > zt=3DUY&?7-X9 r*A2Q*ooOQ5|6Q3mxqD`%AAiv4FBLQ>BJ(=3DtOwMb > z3<75jkZUQ)j%Ms)vQ;2qjYp@x@8{>7XESnEQW3B|lQP(PYV@FRlRzllE~ANuK0l > L_ > z*JZ{F2gL%2X2ihpY39SDl$6 YC > zknPElJSSBUIv!eAG>w{ OCnX > za(JEARZw!k{Lix?3Y(d-Dz`6@jQlo_q-MN>Cf_t&UNh7- > PWV~o9k)+h9?sds1ss@v > zSgaMn{Kb=3D7It4nMlG8iA*- > tv(8s3A%CEq{t;8=3DO zM> y7^ > zHFgs~)y^m>Y0}kz;rw*tL*u)~59P__Q50&{N<`&%MP=3D6xI`XVy(=3D)}zCz;!HOg#uf > z9rMP(?=3Duq>)!=3DO_5T;Xn*ZEvFBS- > w@qC>3ZSgiihc{E6Y0WMWLTM;qcN6|VoRA2ii > z8Wv z5x1?lQ6?hL$xD?`OMxfhj9I3V(LXd;wIjy0%sde`C3*cVk$V-{XCZzitML(}hGq&W > zR6OY2zXDu`Ji`j=3D zeC@D`Bk`qD- > *FcHUgCG^*Ft)$0chR^nj0*^9Lo%3!LPy5Z`8aL7k~C)m&nb@VSbRs > zR)LYvt0z=3D`QnpP5lZXAajLZ+OW6PzbCC`nl5R`B*W7Z0ZIWb}Z3r!bDsjGSsZ}3qw > z@+>x$nN!4f2_$TxpPH`Bp+*zZwIjay=3DkXIxZ~b|bPjC@`RliTNSy{v1hxd3Y)_Mb0 > zbg!HYL7q=3D|)K)66X?l3BC=3D;)1JTi`*N%&6Af52{u5~9DZ!We)G0NrOXm_*p`s=3D9<3 > zC>yv*v4Be2^=3DL&QAcWUxG>cw5Pdc$F@LW<#_0^7;{ndNFeKxeuV4|P0fPnO7 > {FtVq > zO<`-jb>5LvPCzD Ie5 > z&lvKA^zg*GlIiDaz9%X_gT u > zy9GEmH6OQaK&Pm)>b~^3OV#)=3Do9cTrUrt>!F28Jb$xdHL%*l^EeCFD9&Q;wV > aGRj! > z?uUrJ8EsG_!cHQ=3DcGI;Pme{>%$;Opa9fj0Ml@p;f#7N!%k;ip?_lnSkZoQ`nX87Dh > z&K&9P593 (_fvkH > zj+J$59%!#I24A*hWHJ-4_Ctrw6E592b#LRWc>Ptu2scN=3Db2Ijeh=3DlB^bBZ} z9a*MSK}HpE`0@YC# @@5TTQn > z?CeY4{kuixEHc-m@xjfNtv~#=3DdjVmES8qyY-f%Hw1>t_`OI)?|LFDDTFfc|- > *^dW5 > zPnA_GeDOVvSV?J&WdMOdDP;anz2WaI|HC3`e;@PO5o&OS>sSsoAjG- > =3Dt3xxw9x1s> > zVm#1b>P&Rq5mpJN=3D1;DQ8#6iB;fP?(yk)ztorQi*=3DMY&wlN(8NJq)- > ^X=3D?*fdvDOD > z`(JGux-`GaNmsm5q-w4X%pe_@@+Y3)8H?8_`s)?=3DrQVZ- > h^Vc`*v)kGJbP?dUI5#x > z`84_2H{tYJurhfeC?NC40xO$N%S)TU?yI}vs<5K$b^TLS%uYjUS|b)V3|0DaCk{oS > zg(nfG=3DVL~76{%+~krFd)LMa74o$iwJYAs<&=3Dl>=3DC$ao?q sA > zZlF4U%8K&5(v9>;K6BghvpZFj_f|I?V+aV2Oxtwvj0pUDM~Y_7r0D%A=3DCBcD?A- > H_ > zaM|6YW0k057RWKFX()&h`2rEHRWN;$j1w#6y?k}tKvZqO5pdB?bN{VppLLrXq > Cg}S > zLrkk- > #H8FCX(~TuDr3@Czn7;N3r_W(8Cw)J0So_haVPLuFW76G=3DzQV@?(a{qB$or- > z+VMN2%ADrBi_|PspH2GZPrdwBTEioW8OQeX`i5Sb*l`|YPr)WT7V`}x{oHoGvz{ > }W > zX(smItA4K4FD0?L?CvwumiJ(~x~V!UqON78WtP&ZH7`k+k&oIy#6&Zw<~p|qjo > wW$ > znAFU~_wY4f2q?dP- > TA{<^Mn1>h(*+#lDQpI$IQDXBKM?{66ITiV?#qWM5N(}D&U93 > zj|M%z_0A$&y8G65!Wr&Pnd5c&!OVJ9drZP&@55fj2IrWIQ}>- > &vpdCAooZEI#wk{a > zgx#R4{ai15lIvqI^i!SW=3D(qjUKc1v27dP7Z8CgbtcNnyc0*O?G4Lj;Lw-z6u{d(T) > z+F&TQ3Z0eQBat347c;Kh@{>|v>E&2q>r6VMgn|dX3X{Nyd>`XCZuE^3vg|(I;c#~ > d > zb@`LnuXpJq+h_GYMf+*5QRzc%K~Rg{xvlofJK_*c64xn6snyN9CD0%kWBtz > 0zq > z`{T8Kw42-WR+^3%EG0W*<~H~Y5^-!8$pgrwMF_^dwKelNGA()21;G~Z5- > iu6OP@(+ > zl3?OQBuGw%Kvy506y3D2Ealok+8w7b0K-PIj15aS`*DtCSF5P?(p- > UTYHHHGHJa^R > z4e9LSAJ3h-L#Xl#|AfTWd?2PBdir}bJ#*uf0H*de30J{rlp&Jj?wWD1^K|`15UCeR > zuA#HAvAOz|4q>gKvj+17z#t(zN)0g;GE7o0Q*UuksdP*0_5icXGZ9fv=3D>~X8jOO< > C > zIaO6rvgX1Sc)tpANWK;dxZK!_QCV$^B- > aGV z{P2CF_9~9+P&8xNjgy8qGur3l%7)lWs&<0y#B<54<- > CE_@x)deTU#?kEtpbb>pfJQ > zHpmnRMmlhT^0sFnO^j;ri4e z8t|_vqFS;ct|XjQPILVEU_XvAy45$nmXd0n4iEBrh&mveC~u*x6Ty?@ThQ&fO4 > pUp > z- > SOHV7X1WPBb9M>=3DKevHO6q12M?qC}4Uo%$PK2}_{a@$%L0EWCuqmB EqfGQeE > zsxs)ys+*k-al;>afDCY~j > zz0xRppPQh281I(n% Aw_D > z`pX6=3D`~Txca z^ID+>vSwaH^LlJAv~6je8V0!2J3fGM>JE0HpCJct4;A^5CIqv#+yRm*Dk>%VRb7X > ^ > zaEOW30T55U1mdYLACr^!+>)v)D*;`5XwKiV1v3is9wuCpmL9U?d_IkEq%eeOe > pj>) > z8rj(mJCDhJ#=3Dr^|)S^O6D=3DJ;;6?LwAUK < > z+V*l*IUqI7jQnNKWCFOv=3DIkpI{#!D7r|N$A<`AHK<;qzmmGP 3v > zgckP^pN_kbow(BsTd37MKK#{m4Q^eeBGg5J^qQ4{p)=3DpT%Cf)Bn*YAKp Y8EQ > zWv~6Pm)z?6upGJQnAFrAPvnT26jgN=3D4=3DM>- > &_T{>(E;Q4VrnQyjtaxZ$Y@4|Ar} zFi~c_+%%6YTlVtuy8KFA%+Vo_ik Xd > zF@iS2N;c?&GGNFv1f- > nCYX$GGw_pthGcYm1QLmS(&Y}yZ2qix;N5?AwhGp9zFlqL! > z^`{h;mc|=3DKfUv%1XVbwa>Reo6BUy;r)1;*Ix_5nOZ+3!7OcsC&l{Y~<^xnx)FPB)$ > zT(wOsVE=3D(G6j6 TTEJ1 > z)85FA&k+NWVIwM!q-nl6>W!olBM$tvu&<7isI;$8$1LCkTs%Mn+- > iRa;O_hx8v1pM > zytV;@Pd%7Hq5d&IS}DcM&c2crLcP?EBXt6N`!ERRbAA+Idh>ZbBVcYm3!U~#VJi > -E > z@yX%gA!JSy7+fjhUc6`+7#P4Q(T7a`MxUf-sO5jJX78 > zgkxfmcIZ1)Sn7}kn1C#g7=3DZg@{tAo?! fvwHu8795}6W > z#H5eB2Vfbl%LkF(qMU(B+cDO3uQjqVqlTq@ry$%1Z`GMZ3$=3D@W&A@Qm6- > `X2@$m4( > zkAA *% > zYs#3InBRHdV-hebA9x6ZLhn6C6;mKArSn;j3?O?1VLd0)`fsW4V+|i4vChs;N(t}L > zzxORFDtbfp8Q1LVZ=3Dj(c4{51Myauc~v<&~N!rm1u+Ms>lmfMuLU>#3re18H>09 > 9|n > zy(jMtEV}firKOM{ZFFZ>f8@65@vkQ|Xu`Kdpohdjvy;+?U{ksH!hn) u > zzur2DtEs92?jI=3Dh7ah=3D_12c1S0)aLYO_w?y)$)&&4PD&sDENKo+y3R5=3D6%I > b > zGzQdx3&`_%4`1XTX&gKPf@QdM5NQkG00{@&ilb@etG><|x&R#Xj R)a;LqQ > zyP{CG;No^qJe&P|%j}_Vf3m=3Dg|Mjkaq&zy$chr*fF%1y$LsbMMByeD`A}PP&wv > Utt > zAno82YLb`qd{zS|fA16}(H`uS*3JF%IKYtVI9L;Ju$iiS4_pD=3DnGIE@Rk?e0|BD4c > zYjjx+S0ZC3yJM7@H*Rw8Uu{r!z&Eo%^_zRzhKAc0S5KraKE- > {)&!(C4TBm|f@HQxf > zev{VufNzF|+;t3a_1zsb2xpD&afB0_JqOR!YcL97@POpi3UOib3JQmpha(xcaTzx > & > zOu$Gx>L5_N;ING7o}WKa(L+zl#Sq)w)#13u5%=3DW > mbM0 > z q36 > zJ-V#v@2uVN;-fO$>Q3EFRGqzz+RL|C)SEv72I*ZVR7LxhgsDEe9(zERxLD6-LubBc > z=3D{sgH-$it>Zu$Km0eAk{IXSQ{rXX~7V0`d=3D?2067?~0;;a@s`iU=3D+C0RX#PgRugha > zjp2*{uFE&z@GM)tDQqI8#mUZ4Nq_fIA7tJ1kI^;fz|)qZ`~gOeqHjf7rM2#VrNDh > g > zB<+^ij-ki!v}bU&*9f|^qzv4K^(1pB)lDoR(e>s(&CJBEz4iA#{Ohp`r!LIk5#>S$ > z7o3T5Aw?;Ir~Q{0{tan@mVKmXfG!QilLZES2RC8c$ 2bs > zWU&a@*gr5Yb$VC6Hg3V%A&i$|r$ln0h?X<~ejxRkDBW%3bjkL;&%&3+H1#(t > _TQ<~ > zPpkWLsyFv=3DkpB8wMCMJ461d6- > pCbtOKF}LM597LXt^A&pRwRw98q!!F^8guBWqSJV > zr~@V68iN_D)Gs;XkCz0TsX- > UHQ1Mi^sc##}gBjVZTlZpe_MN83lYe%tK`Sg&@uw9Z > zIQm^k)g4d<|30ZAI|(k(W<|V6rfi2w?&bVP=3DE|GYt!3o@ > z?NNVoMqm94?a?~(RyW7K;mU q&>D;Lu4Y*65q0=3D*^{N > zJ~z_keD3Z`w0~0%emEg?SSLF}R>u*S`g}&TwM4eMHWIfO;RJ(Su)h1YdScE5FtT > bF > ziDTuJrA!y&f=3D+^gL|`TtST1?31}M`#moTOg2qe`2_kyJVP~zd&y|78+zCZKrXIHv< > z)UF|RBN|!?16XpV&dJy);mH{rFB#D^b7oDY>z&>;j=3Dk+UTyIVH8IGTJgEn2{J>XvL > zdgjTdzgd;q6>GA7MEl@M*wCdAvKV&S`|CX=3Dt0%pehwYG>fgEzqD%AP16p?Y5 > g^7sU > zLf>7*JS&k}_6^20dy9IbDp%7JEsuW+wH!W^lEeSqNKiomj<^3KlckUhznKifZ0*> > 4 > zE_!)y=3D_EwhLGL2K*Z6D7{86tEUr6Af>rW7a0vsE||xbk=3Dt`! > zzUkq!kCo%uLaCr2D0!!GM4dVBn`v*$- > 5rIw#A>R!$S=3DY%YqN6p8ylZm`2kn%InZ;K > z7(3x`Ee(xdzXG2?^=3D>&bu~p2sZxg>4nRfOVpVo#cI)JlDzLI%-1#f$3B_;fh#D$u=3D > z0SPWgD^lq8%^ky`)?URe2rJyep=3D)<^H- > JdOHA$qRsqPN|9aQ#M)bXn83O8fh4VJ)p > zWC@ZRKOt8~{#dP46~F3A$CLt0er|(RLGgCCg1<&AieZ_Y(_nlcLcRN@pv{H3#M > Z@o > z-uS-|mI+0|HP^V@kgUr6i8JxWAD=3DkS1>aE=3Dx;G-;I zHP1~KY8PZbwNa<67*>#8q+12=3DNL$%Y)L+k(Omwaq`GwWNh3{veQY5reoo#0 > G7mqVU > zZ}4gfx2=3DtuUW)*$8Lf!xV*vrMwbtJI>?ITXWyR<*^}vJ^nd>Lv&VAoW C^ > zHI?xRN2Da8K#5w~#^DR7VyPCuJBujkzhiio22&X$G2_PSQ2&;$Sdk>os- > Y* zBgZYqk&sZNmFl5wY?m`+eLu13SuH+zkA~TYqjSx!8OhT5*dlRct)*Y(S=3Dy!* zv&u^91`JfQ*$6*pQVC-u91K$*C|OqggzBlgrS{#d6}kP8jk$3_NjD(*bXW$B1<`ZT > zM! Dh>thr$2oA7josXpNOr1+57 > z5jy$&FT~Q#hoF~^`tk^(W1b}FA(;i)BCS2Lb(YG0+rOLId=3DLiC(7oJ{dEm@R!+fm~ > zUK-- > r`G+6m>P#CX^^gGwFgFWdb7NfYzw7q!X89+`)cz;PoNcImsxT{?GExC@YdV-z > zQNMrLPkc0Mq61&ieAn&f<1E@hBp2U~t?*vXFlD!!lO1!t0DGb$zn|r!Iu(@oDY > VB& > zn>g;JziSl^ICap5w{h5#eGWVB$J2=3DSx~AE z > z@o`0!- > ;pxI%3>miLvJ(*K3{AHTZZ1r@s2bxPdi7ht&W6c{iRzPMlK&fVT1^jWbe`m > zSCR8?6JGt;@q->Hx=3D&(%y z6n+-tCLkc^7WwalvMRxYYpwA9e_DE{_qqMceQQ(zjH(%sZ@Ec- > |Ni}Ud%P6Tnb329 > zn)?!Y8;8Fw%_7((T4XZnVN^5r#00ct>|pan^z)QWY- > w@fzc7$VQ4tNhm5V8N(SdPS > zemjgX2%vWp%#H=3D-{5i+AdGbyyd-I-k#r$ed0s8()pll8;CnqF7-8-C&&+ z`muCcmU}w~g)h57u71)|mjZo^rF{9~IwTdpAD~X6WKA{T4NnvGg&hsfYb6uSw > hf!j > zHoj%V;v%{IZ#*G1QUmD{KDk(NxO5Abn&JHhr*xFazXZj)!E#;GH#Z}j- > W2=3DmiHTno > zX2~4?ou7zkNi;j=3DG2vMODkB-GC^A0To|hQ3vXLfA6;mW-1%->>1- > qeL zOx1EM0#4u+<(ESu7H_=3Dm$RcREL?j&rJX$E469B5EpGLG%E_eUUI=3DkmW%(xCp > wM(WO > z`0>B1JR`+)ZDiuFC!F7Fv- > o{nNs}LjLmgaAC6@n7U0(G&%#Tte_%S&pL+Q?SKoF=3D- > zDtmlst&$pckUytb@##P0WaH?Co1;akE~Zvk6#AaOAIct)4pU6&*S@Y5`rEwHP( > nm$ > zgn7NXKD%P4(&M*qR- > (Xa0ESFjytwle?((hbQ(+J%HI((eWV?|UN_$%s)~I_M41c

zdl5+YVUfoCx4z6{uA%Yv73`|Qa22(H#Pe7av- > 4GBy~|DLthW;7TXboX > z-5&3FOX%1i^dQ2GnZ!Q`s0&McGxj=3D7?N$3RKF0@SL|U8?^U+q0i&C- > ixZ#a}(P%71 > zoBx|Sf4X*XHj^d$b^%BJ$C%pg-_(?ktAU2? z29yKpcnd2lKD%*tV6pX?e>p>E?CYBc=3D_%k(;7C*D{wNxkOC(}($k{4(TM>PI`LAq > e > z3T7;){DYiS#d;XizuxUJL$SNo@~Ysw{k{+W7ot*gVo^s(DMlvR(W_n|;V;z!hRps4 > z=3D*8)G5{YRZrdlssB7+G)ktn&em?Jdj`2c1TJ!IWnsP@e)v+#1d{xGOk`|zrKt5H`8 > z6E%O(^b~(EUXk%iX#>N>Lc9H_ln{cN(pT4P$E;ENu-7UEtjFzgP5w#}NelsZ%yHT- > zH|7YArh@+FLkg- > WyQf46u7m&^N)HT7NdQ=3D}0@MIxojeK!Md;ds_=3D`o<`7w}3UO`cs > zSq9bV`0WT)a{;h;xTzQsZYz+pMj(c>AFQ=3DDSU1aP8_^Spw^*etyn3;Y*umJCxLB) > 0 > zVJ!(NVsq}huZVy?i_$ETPJ3j2dkyQkSflDW*bJKun$r%|8UdN9N9=3D#B#Ffq3JXl25 > zwYj@;E^4?Qbs%M^x|g>zUX- > moN|$od7~drZJroCY)J7+eSWPy2RV{nT%6_5A!IRJo > zi8=3DU=3DZM=3Ds)cYP!LsAB)eX5uM~{k1h8ZA^WE?DkQ}- > u$+G_O!aRmQ{dO`@hXLwpm5C > zNU0E*bSwaA=3Dy 6Ob;XHAbX$!ujWY~NcR9fz? > zA{m&^E5y=3D=3D&`JNCtrsVh*i&iF- > ?WlG(O`6`mEL_JQUgwBaPs9#n0&1?v~&GzzdjmI > zz;x+PIX0*)!He3B3jXcux%i`s$1TSzmIhZ|A7~p-btFsIE3F3J4|sECJO+3NPmT;r > z(`8ufm$uiah2N>{^Qhe=3D|MhLVp0?h)DD98pPi- > 6N<6qBRAbW>R|5Yvc<6M7$Kw`6V > zykER4;vQBS=3D$^%S^!I_lh+2`B+qv_Wb+~*G- > KqAl?$!d7>ZZeK;OxmNkYMvFp2q4` > z*_X9xhA4LNtQQ)&M5X98{Cc}h1cMary%8+q#Wnmo$Qymy;)fmc|FZd?6#lO^ > |E7=3D& > zinFr&rqO5i{Cmn- > mAJE1N!glf#G~FQLF|qUdbujR_V?>J#%wH(*F9ik_ow}}_*Uab > zHc30pN2CH7^FPflV`?8Lkh=3D0-eEKCgLk}#seh5Il8IODKzrzPYl(CeA*!pA4!Y7FO > zcNK3hn^UbWsG=3DE5oFhhLR01NF >R7 > zEEjGQ5}5DUI9$~Ih*~?gcI)QdeUX3BCj2aW{>4V{DCIy zewYu%j%O4VIkhZkcWa(@`nbBocs8IhjubbMGdg?)z*90m648D6AVD`1)- > %FkTXOBB > zP|d7HzCfgS*RL;hANHz_r~p!Hs6y0ZO@+!h^dm<`7mZ%Zof7$#MDt_Ao4elt`& > uLj > zlTA~)`J0{6{AFmh>r<9;AS2ova>u)4Z1TkboMc&8vdDSB#{HD%CBS8|^oC>W({| > *p > zRlWes_wf0Q$g*lOC7XR8&417+%2g^63_9()r=3D13yuP|1G41eo}^X- > Z#BGRQr2oaKQ > zd#_$+Bg{K0^Up7W4+a{1r;$PL8Ssj_G$i|bLgIsVR!0QASuzrQ_;{v!^GnwR99Kt( > zbzOY7HA@@`o+0LcFXikznk{eR9I5V|q- > =3DmGP;tP2mL@wi(xnoq6<%Cb76W9}fNy@K > zF4uDWWX@y?A2A7~G&*V}EuZ~zTROtt+d75Q^yxKVtQ@q#Ubg5IpK8&A(0O > pL)Fq>4 > zErMqAnr~LWvSvJfHSe1W@PUyj;UZ2At;;Q7Mj3n(H;m=3DZyFLHX<^K(SZL7nG!yf > Mt > z7NpD|;>s!T`pJy9MJHe@%}s Yi > zCZ%slP}8E+YP1XGbQS{uKHUm|flMd6J4@rg@~QrbJf!8pD35rBzsLrT#(YT7u E` > zjUtVz MbjCYgyG44iG*#uQ@)u > zC~Eo0+!?wXfF_mS@^V@9URG(UJyZo$#Zq8jtvc>xb50M~*bS8Dvdh%f3zOxi4 > PQQA > z4N6Q0m5KFPLBQoh_3H|C6MzE&$Q904i+ZL*IxU)qSkhGy1@<>3O{I0mF5x4~ > zJ}o`^kZknMj^K^@pf2%K;$BncL5PdD!y$`_9jPK>COL%E;+~G=3DGdHZ>+ERvEU`D > z=3DWqq$#u+z+R=3DnqR{$^T7btF#I^z1vUF0Pnju%bYC6R{)QVoM2h`KX_$HLK&~F` > F4& > zPs(%lZ$`Yqrg~lh7x;m4Qk#E}{JsOmT4;3ixhwytTqz0e0X_o`J@$XW@|e9& 5 > z)u+jJ^U&eKVDByaVWZoE #~g=3DO > zh9{BD_R(v;_{}ERQ1I1{)S~QkD}x9$yg#cWJ4BpxESCfGRavH|BW13aNZTUNI- > ke1 > z-ZpJYw1!U7&e`HIVD~KgfNWY`f~PR5eFhBJF!X@;DRi-O@I~^x{;?=3D(JqlKKAP)z9 > zPe)3O%QP@$YFP8XD+^%%H *F9!4P^ > zyR*NJ%fROlEcM9ylHbSJR3%{Cx(Xf;^dv$z8$)CH;$?BoY@-LMnpxxcdeCt4 zeaXSE9Sj!T9KIyL x6wR > z28k- > jcUY5ppm9h3L;t#(2GQ#6lWs`@p(qB|7<)eFYmFBgp@wBRt0RPfDXYFp%FUzQ > zn}6@up?J+S92#hdiV%=3D9_J@fH$KjmA$&^W4>W1z~Zrh3Y%P|~N1U)EaR+vb4 > G2Su$ > zH^C%0{a1qN(iF(+(w#;|+rd$r68U3Rbui~QXFz#$U51!&-TpdZ4Sm%Z^!(mu%Q- > d6 > z8CSq{iA^TAM+Sba`K- > HlTk6WjIG_wd9C79k1pK0eXjm%0^IIaM;|$XBS{d|BrFdqw > z#ofha9&>zehtJv05)YpWl;=3D*6Vj}fRiXqo~|NWMDdrL;baJJt7$1m_Z)2{i7L8!Ci > zg!H?V9hYfEzZ>b2-$<+b YD!;>m > zjB&$De#dJ{mHM*pwbUUD*Pf8A%@rNWH@7AzTxQxg6mG!STMF%$$lWW > DlU4VKO$Q7# > zj m!6A > z4@VEIi?>7&LjzOGr54aZ7ezt8w6N{*-Vgc2$XvJ4eZQ_!I8 zOeQKB;KBO9WF|DtI+Ym)isg^KtRNur51V}joUmgP$GQ1diiHnwu{`V6IRojEQtq > uX > zZF#mXp*tsygBG?5s}v{<6*>>4To+de*ZQ3rp_|9bMm{m;4B!x<99l@O*Et@=3Db > URoM > z&{&Ho;|`1Me}NhH8)OACAto;^XI|Bo&a1pPpyLC@`H$(t<7;ou<}Y- > Si5+SU@T;dh > z%rOmQby{_IZ%+E&=3DZ&z>w+wbr{TG8b- > +I~rxJ^NTX^hUZQ_xQHPN@RYRbv=3DOAaP0< > z!)K+gHS$Fe*lfbO*C|+bqW4U|T0xD{(Z>%k3p+Kc_9bMkM7eS%=3DacGlFamrzIv > Yry > zy!CUS4MI1%mk{SZtX?ng6^x6BeANh0BRY7- > lW*C|d@!U{tJ?&fR|s{4iO^7Ls>&a* > z{lR%NB>eI0($Ie#o3CF0$0n?#{5ID|>sUl_n541g@gI%;D{iRy=3D?jx!x%11WEn8-K > z17`}vk;|Fgj;{k?2BB;r<`exSpt>`x>0G_? zJp$cgAZ(6J+lT#|aI%6{HU`5~DOLpAcD?3v&g9UyK5T}S;RxPvAwB|Kpk6jKp|7j> > zcmodM5g~7gj&}=3DaeU|zkHpu$9{UBdyFz%8LdY{G)GAV4UGAe1n)jM4U`9vqW > d@XHe > zvn*f~#Zp|_nC%d- > |J@ekX6y7s%ZdqaOcP#sZ2A?~^~pHh%=3D*1;Ol&FTn*PyU` zby0_YlP8BM ny< > z chHgloBWr|bA > zMKPos+7`2YmR-*0?2%Rd{F|r|JY6s!4dfPQz&>Y1%!Z-(ytQE}L~vl8pUl{XkJ#~v > z;sH3X1}Nrh(Y9Hrm~B7jTOA+;D$;{1q)9UMo!X#p>uRu y > z>7l3si>V5>pfBw8sdZX#?`q@Ws u7Dn > z#$NH(cP=3Dx%L()gfl@~U(N7?bH-GZbh^enr@6%pM%$=3DlWVk)_- > YI>c&n(Yp>Lyir~@ > zchZ+(5g+cYwVEu@^U3%4MzZJly-DN}4%m{;HtH&Ur}UQTTiu5LT5eya<{dT- > z*&>EVOnq&ad$05T2- > 6~99;0)?O8ht^W4XIVWE^+%1QjLTmw9`uGN1TrQYC7Tcf_kK > zHG^0n+f9uW V)~VYWQrveHMi > zfN2G(b{o8^p=3D!#x#k~@yQd$o(uwxv*37^>ZHuzGK?- > }0}e(EkUQx{gt5Qv2GI}9Ks > z!FA+PVttK&n5ITNqbQ<${(Ny*IIvO~r$yUR$LHq}YA_20_9&v5cC_pB{rsHv{IN~| > zB7M#sI}A+1R|XHK8NN| YvY > z0|VFw6kG%(6tX|SI! `VxgtihK5w!+4bR%e5W4fcRUBut}=3D3 > z- > )H#z{^EH0s&$?+rVFb*_4hmDVBE4f&*f)m{)Z8!2gv<^3>?XepM!W9v<{Ch*&M > CZ > zEF7y{JOhdOG5S)Z($t}Bx#?zxJZpFAj{EoC6acxAc&- > zkPN%U70?8E_}W_g4yR3~ODER0cWV&_yMN)mSf7TZmJYEJJ;(XzaN}1{8?lJ}#(s > m; > zW|v15IS#dO9dsVb+`K%|mKLYQ`v7OWY9)1@bdrpTxA4UelW$>kG1mb(AEzc > E-_;{w > zVv9Mg8#y&_C1KUXv{3&vsjInK?P9k!Kw6Doi&0 !C > z&Oz)d>~kXXEBt*YUNYlNg{`OjGDM@t@vB^MR!o!L29CfzJS^M(PJU8iO1MT!h > o5lF > z`$pxAzRQqB;CxvAt)ReL=3DDy7q0zA~F%vdm%*C2G9QrcWxaZtovH8AUID)+jk!+ > &#` > zu8PN9zzG<&w#XEzPu3k9uOxp3 m$*V > zr)B9>vPLR=3Dp7%xB++>>JY`3Ym=3D1cbF{hIeo;_%=3DveRLANh;W{kPO*~jh9Ym=3D(jpt > a > zJ#<5w4$#12>wlMDi?Q+qCH ~cD > zL*i=3D?4^towDW@6ZZvX}XxV_8p+N1b-Y>&-%){*~b)fp8lC7 K0fBZT$@>U5( > zw~vYQ9)g+mMNjJZO;ypyUQy?DgnWW;aSj0PJ4OwJ$*tKT?cr- > }{HlC+M?0#F#>i8( > zj(X%WV > zS`8a^L($nQNdP{rrVC-8TlIiTojFTos2}fBOiFscXsX)|#n5?>gP~&Z3#V=3D%(_+lQ > zmQdIJC^%^V$8;U;ldsWo2VbENGMHcf5B@mtFMr&y(|nzbcKOwdz@ZiC4(uw > JY7J-w > zx+Ks20(N33@8E+U|KdOToNfZdDa~6%EP NQcQ > zsIteH`BI8wvcZq|2;Kg%_Xax|fz>xP{dZZj_rNL>t_;(&;OH1@qf#edpPOC6I;7a~ > z{GC m > zgZ^v74!sE+GXZxT79YC > zP(- > ^pt9m>uGwPUJs;%Dmc zGH;^fyr(a?>xrm&;uh2hKqp>D- > AtFAT>ifRM4qagM#_ICS_6ajaSY1HT~q@|ZC7aZ > ztaZ-qOOaLm%t6oZ9YOn=3DE(10@DEhaBQ^B{m;J*XHXO%^W- > *{A1{Q)=3DdfP z7o9Sr&Hb+cKp_J}DKZP{|HcjLA$T~e5=3DzhcHzQ8Rcxy#4_Jut8O5$*1Tc%4Tuha7 > z > z8}&Tjh;wZ^t638Hny92PRgSsgD|&`v%!}OsWN8K*Tl~yqWVJVu06$yrS_UX5QJ > @Ox > zT;Dpg0?rG2zZ$zEGV%BZg?BCcOw8f0y<7`X6u#1)S6Xkp)M}F2uC?#KSb)e3Erk0l > zVjM=3DQw37|BK0|)-NN#aLCS2l&_`Bh{)byonNa}o`#PAx_!nIovQ*+3JX2{Vfpr&Vj > z@@D|n_ZN4O{9DvE7v{&bXg=3DZEU10j+T06D%-{92vZFC<}cxt{&w}6A$>f^vt- > IIUz > zX7s$Ye 2h|Tdt}MtL > zVNPUku78tD_L%o0`Dm5lH$DhZC|4-Rr- > NH4*=3DbhL7Tc zvuweI{|9?$MSHwds_Wa=3DJMt(BCJm(Zcel=3D3F}}$`b4q(=3D5Q*e;uls*dp^};_tE-1A > zv1;CZ!z3UqKTUkaP--mr9lV{#l6K~@id`k38|cvYSujvU4pug>^T)D3xwFOZ8inw* > zJq48N!P2zA_Le>{HRZryvx7sxFBEjO- > t@Kt=3D4N+y_aVRwp>sWU)5nch6!_jmvXsEg > z%$!+V90Bl80U=3D9#Ui%n}MY*{_G{_A()IK% *M} > zXkHveHJ{5oNB!|41fZtO(8;Czn|qG|^5c>v=3DI79ux_mW!$AgIV4X~k} z|1c%UOZHo1nvp%Et%%VOgL&N@2tA7**#bGJG1O&e;>s5<_*oh?*l0V=3D|CLrZ > ?;v@B > zFX;42CfFR9Kvq~%Gs1tQCKlSdOh=3D%s4Xwrdnxv)GDjtvS`J`;LJuYdg529t7-fZZ_ > zhIJKd7B<@x=3D&7cQcLS_cUj$bRbNh#&13uJi8UlzLWi74P)^>EXzpkWPp;|BJhb) > OG > z#&Ei(8!pvdspXyV=3Di${Up0hAgi5h^Wb~1?H{9pVD9Y@}gsp&{O?oGe~dN n{X > z+n=3D&#uw4x}Qr>^82O6n*dqw;mEZ7*Vbz2o;mCVnNDb;%aU3w1p > Z%wkIe6 > zemi*WCg>N{vcSU~L|t^ pR > zAM#^HG4{E=3DVMIXy1KC6L9~`NHxIL0PG74OqKJLE72EncK6SK2kYcmD23Aq75!s > SCv > zOI0V)+9b%8^ak8NWz6d- > k{d)@42l)WJ2A>GH8p)7XQ$wW&!*&3( > zh6=3DX0QmwWX9XHj5l1@tYGXVhG)- > Zn#wNpPQp(Ni*tcC&}=3D@s_9N9{m^ z8x|ZZV|V(- > wd4GPaPN;R*6Z=3D9B^dVhCge(M^jW3+2jd}ku1^OwuyQXhJjp32hHPsf > z6DIzb?>7aA+He4^nN0%FUx{ 8V7 > z{e3`hv)IR)|6IB5)LOXZbEaOWxBzyK3p*{KL7`B{M5>R9p`js?G!6Gf2r%(qy!2Lj > z5_5g=3D+O$i$k;%- > 1M>+rpv2FUU;oAEoCod+I4AQd1PxT+aTHr_Q(XcxtT82_3RO5%a > z&OaO+r&Y@1_u$4#@6Kr5r`tgZ#?TY4KvNy0-So=3DW!Xm2z- > qZ)Dr > z8^m2cGOYXS46uyhP4^P*=3Dm&INK2G2<@A{gSRyt`X3hYM1dK^fTL+}^NLZ^{;EN > pfA > zzk3E?iA)clJzkV-SNG3`oC94dvEie24TnC}fAb4SCty*R|Hd3Fsy^+To{DYevJq=3D0 > zd*)2v- > xNKm?zoMJ$O08k5I%qLA`oC+u2@)UWgnwP zJ}1WN3?uNP2F_2WYW;=3DcZNmD{HLl)RQRz+}Ap(|7PfhWqR;jlEOi+H!az=3DIbR(F > Um > zF^Qnb=3DV^aGoVi%p5@5dasXN&yO+sp{#+ONfnrQat?UBs}- > _g|Ab91$j2ad2jnt+=3DM > znjCz(1JsHajoS0|`t>6;eSLj@h^2*Ah*VpG8IY#cF*A8@u>iY zqsUY > QmA > zI8WBm)>bz$fvDymd&q+x@}sQA2c`G$?|`vCe}M2pf > HM# > zheeBy00C`XV^EG!sQek p^Ljf0`_wKX+3QU^wzXrqcJ5VI=3D > z%LFpiFXepsrY41q`+wT|%BZ&9Z_Ub&wpc0dr34AZ-KB*Pv``$1yL- > `KrNzBi@nXeF > z&|(SD;vPIWMN`~bT;|Z3|ID4W?wy&t*8Mo=3D3o9h&J?FgdexJSfv*iGwMI9ux=3Dr; > !F > z96}1HN;3idgO@ Q > z&i+6o{$dvafj~~h7y%NkV8YJO3iJNYWD(FK;1&jM(<$%k>I4u zQ~;Fp`38`OX#rGxcXV{fbKQ0Ta!s_z17iC8Kx&(I`I|M@3zgG$4`qDcNiH1sBtD#e > zpR`hDn8<|^gYp- > =3DDEZ9aDLwe&emQpH(ng8O8f|N|b > zH4zVF=3DtWGAj{(Yv%El$BwtDY|5NCd~!c%~RNFsPLpIkax{FbE+m2e*mNCEGLO- > %SX > zodxd92WpO^vu+MB(Ot!|X5}KHpF44af6MbrA9T3jq;nSxa^c2UjNKutm- > Et@op^sZ > z(PV4)b@KbiS8rjzGw$P?ecXItw)q8{V$j=3DeyDxYoUV}DLJO6Pn`s?hon36WXmcW > 5K > zeC?m*zsJ3=3D=3DoWF0{f=3D>$@(SQ?X>i}WprY8}^c)nHHkz?39XcE59xU%zt8YcTGes) > B > zH2YMPwJ!112bA#&L)YnAHDB}U7u 9JRkFbj^Cvi9l63 > z58nGIm}R}|M7|UH&b8_z*ynv1aq7iMqL7x2F~ms(rw~N6Jaf9*{>CnEEbXR8v+ > d4T > z!H?wY3GK5;*W!A8nSjWZMeQonDZ4tS9!9|d9K7}vWPA=3Dhs>q>No93mL!zR8^` > QF}Y > zTI!eAjaQV1M2^`)i

gsTGz- > UdJ`7B{!>?&PTBz9N#IZ7Q}*EorQD*yw`m46ew<3 > z87)+@@>(M^Ut`PfHW8}|%i=3DUCc;%`Vm > t<0 > zY19ko`T>+{%Y{D8hI=3DE}P4~qK*zJbkX=3DpXoWB=3DtV8_9Fq>lL9iVI7j1nu91wBMk^ > + > zElX15@Ap6nw7R-_Pk(=3DYZSDB>?wr}z4@Df7I=3D$YkWCI|xaIF4J>Aunc44=3D- > Jy}mZu > zVB~u_KPuVpT*x^- > &~ysjCF > zL7?|Z(k1dz6k?tZnBXFxqxH184E^{C=3DwY>+!4Bu9r zdZ_HM{s(D2$+Aw$$&kBkE&gqbzpoI)JN5kbrU8XeX^- > Xvwp0G$Ypd!Rc|ZQdfy)>q > z?BI#4t8>*l+N0LsLb-u2@`DW+7Ms2E^o~CDw9eY+Dim&9i=3Dt*5M1? LF2U(N;ve > z`|0v08lJhE-43hj7M93rCRyY)ruFz>1_a7|nX6fl6HUP*{cap@cO}UZ@ z%F4 m > zWwd~Q=3D4lm!YtSf_-qES5MxxWX5+^YjA!%d?VI%<- > L*AxFh1pz z-+A1})>Y$ot=3DtBi7`EBHM))04PgdG!C3|W9<{kXTRqd-l>MDyh5Jwv@sO|fU(KJG; > zpy8K08Rt3W+MmJfk*P`OaguoF$9nZ? y|~ > z00Or@+!SI3@2(P}796xp>J0956C(_vOK58T&>4}Bc#K~++)E^5G(maiYeE`Qw>1j > O > zfyrt7iK^IwEQQ+J*?%=3DtrZk_{Gy$lkm=3D9OHHy_pQQOeOcoE|eR~ > #6 > z+q=3Dps>#7&(I^=3D|n(|XZdHF|p!RCT&7W > ;)# > zTEK|~ccm`7FiosvPELCIOk6XiH$ch>A#g!g%l((Twgs9NcL2xZ?tVVqRX8Bfi7*Bb > z$n8i2c(^- > EOiWof3)PnW_^kmbSk2ZKeVaMRDwzdd{9T7PtDzfPTc?57m_WSGN9%7z > zFB+{S5NUxY@NrVg5y?0uv}1zWMw1yumux iap7Ma7+~R > zr)wN#;gC`Q+R- > b8%JY{4`p6?)P1CDtmZvTm7K0LPh4s3mNRsVKv~x!vbuA=3DEyo7sn > zx@d9YxZQ9&G%`VB#)^`&cT-iR- > irr8)!tUR=3Df%wR#a+fmaF+6kzw)!tT!&ux?VwHK > zuCRQCF M > zlyIFB3)8W{%ep1;@3Hrb{On=3D^UXH!1FF6ot6ZH|@2oO- > 1ug!MMyK4jY4r>~c*wq)=3D > z5?4L{`uv*hRA2IQ)4kErSS*9GunX_1cOFTm#^+Qq0S_N8aP5_s1a&;6i#5R*bbF > S* > z#Db~drQ;Aa&eVWFqd6psvJd3Dk?KJ}Uknv@r3)=3D#4fmUbwUKvs`O|A_AKkpk- > _@X* > za*eL7?SPMkL3#Mtf*%z1>q~Fm^O(3$n5ulY#_zgm?kq8Si*v}taod)R_f57Xu&~I > 4 > zMe~hi*!yt- > e2@cGqtp;X)4Jnhep@aWVS>r+Rl}i5gWL(xnSd1YyG|l5J|Z@riBWB@ > ziu2548b&%$ig}HP+<|uY=3DW^UO9}UO{e0b aMs11 > zN|%vFr}N@XKRz- > JK(IvVlAb%GHn!aQy^#%r)@n*RS;GtVOQ&_lYHpyA?IeW&XA7Ak > z|BUSqSDI+U&nx*Cp)_JOt)3ZsYS9@f(N_=3D-In{tAe0Yfk<0~_114e<1n;vlOL2uYM > z{((;&-ix@kelI*^8AameWW5I*s&~SkPy3v1>ZjEO^wg2}iI@`LHGrp5{`?7&l|=3DiB > z2WKYg$hz;*A)npLAqFDhW~J$~Et+K@5LzY|*bBC`{GYy`MKz}pP*MOH%`Il^)S > KDX > zFMo0>$uK8=3Dq*ajp^RTu-)zze1#Eo}%IW~w);z&_-S$uXmO*Q>V`- > jSejso!@e^uJ< > zH=3DHip!+OAv4%1Y9)7 Tu > znh9Vx+WVghIM d+ > zMY- > =3Djqb9OFw`mb3_o8*Oxi?pBU?8}!{o7@(uN3xlO+=3DM%`(_VSDZy2Oh8!~w+k&+ > m > z%ZWm1z4nMfO?(4A8G8!-oa^F)^bC6>Lk+E{^unUcj(<8ok$m ^I > z9ojx%v+LJ=3DJ=3Dz*8Qq7pk7`FaaSmSh2qYT$=3D%j57^2vNM--7&x{W- > 7FWTd$s&4e|4? > ztQSQgM~N$L*jm4yQu8s#{6W$`jxn>E1hV`C6L73}9SzVnxwo=3Drr_SPGbV#Wtj# > ^0e > zhFT#E%_- > ^{%chJ(LL$H3s~fl9EeV%b1tireNWgxw(yS9Fh10OF`DDS~(@Q#A;E;aO > zX{e*o%yIv-brwX?Q~YrlE^JeXlDw954>|h&G0jx3Jk`q8wav0M)6c16&UR#e(bI)d > z_4)OXSo-JfPgfZ(AGe2R-YVZBJ+*!NP5p{*n#PdT$%5CcZufy+LZJXLltOOg{bL)B > zN7V9i&*xacc=3D!0|AknOH#GgKi3x!mF2GoC<(#rX?v$__j<_vqly#hA-+T`x7hrGd7 > zZOWlBGF!2ad1;q&4^YkJS0ZOaAe_m)!p5V|Cb(G- > tP(~dNEPPRy<$X2&|cAe5A}bI > z)8Q6FAigLwicp?s`1sPQ7Ds))BBpf(C > zspM*Q`~xVaHDGf15Y#}Cmk@r4dII$}U>*6LAQk~w5Bal&&X}hqBuvN%mM#g > 8_@a_! > z=3DSdD&- > %dC)aGBKy)I4VY0M$&(C#^RUo{0JFU0~cf5`J}7vtU97C` zuO?Fsvf#dLyUk)97+Q+_^sF%To)W`5L}Pa8vN%XI;Aw)~K{nJm;aV`gjO=3D7MR`q# > N > zoo}Q$dj|=3D6MQ5saUfExZ$A^%%8yv4=3DAnR|fP!@YkmnZ!B+D_k}hi4|v2=3Dag@ > M4b@Z > zXSZvIU4G`V(uwN`H}{O;cDm~aWbK4z)=3D`ORx_(5zbD- > D#cJI{S4sKPkL746lL@;AW > zDssgPVVyB-*vr3ad5~B- > )<7A_;U0B@WNTn0Uut!})H;sx$}wL|$6|C8Ma|7~&fwo) > zF=3DJ9ad_Vx^JlO*~mkzgt&$$62$Jp`h7H- > BNMSM32B_HnUIx=3Dz%Fof0ntPF#j9WWX+ > zH+7Nt?bXsihnQJ&V*!l;8QAqxfz8zG2s?8DdEVT1u > ziyucuYfTQ5>J=3DE{m*GXCHry^)3czIoupqq)ussRDb$a>gRm`*@wwiVeuPL > D0 > z%@OUSu;f1d%Pvpc- > E1%2R%~!dk=3D(PjT=3Ds4#aYid~`?u`IC4c|^OBxKKwKG976$T2! > zdK(Bjx%p|zQnoY I&smoWNQ`io3z3exz33#|upcPPe9pqcjbNki > zQ}muu_e_p1GW)>}3e~L~cwX>z(@WA_pt;V{V~`YoPVwl{mP+Lx2aXx%>(nuP> > u{Cu > z z#jIMcHOv@}@01qsK4h(a%J}?%jzw%?)We`sq;M^5YGBI;^;KXJrjq W=3D > zT2uli3ePmKF@Zi-kJ8f8th|_h>p3z?rYl%oLl>I8!}A*Ib&s}0-@yHu=3DO|^RYZE2p > zP@V)q P~U > z;q6G6Fl=3DJS@+V2SQBf~>$lOn{j$bE%e4D1Vnqye&9&>PX^~&{4Z$q3$3gL(rPS? > dZ > zS!3T-wQgkxi3VLQjxyVy > zPWRxPv4aGQ`ceMu?n3>Nbo<|EGi>#Rt`mAkcp;LT;n&fUq)umB_%qb~z}%Kod > Up5h > zqp!T4LK? swKrJyMhv%}aG9`Wq|8@~Fbcc#V1G$}omy4nrr| > z&dkRrl6SI$7h^TFoHo8KET6czwakmLGx`ZscDjD|Xe_F!FlBUge@gE- > >+H?u5?g$M > zw}(VhAMOlLkvF&n@<&AFEgwG > nyO9 > z?n(T@juz&gyr6X_V0JPdSLc6e+@hYuR#uH$>B!E{+!lsUgw*6xrrQ_85yP&`<<=3D > QC > z@~WT(uOBk~4>fgOyqV$gq!A`vHLnZ$pElH+7c4kP;{|< 2Oi > z`}1UT-9ENIh>;zj%$I$?o}3xtpbKBuSn(l5`xhE)%Wnj**d(piT36#KJkz>T1Efcl > zWOO$#@!GjrGM%WO^cRI+ > uw!; > zsCmp?gl$-++k3q>;>lh+vL;$63x&F$EUqOkR))%!l_CT248JrU%W89#fZ)YJXI1SL > z(~1OHjO+(tB;@`>ZL(7N$d>6N$&s0!?2kpdhuW+eXi11grPpdDBN=3D(cv+_6{GD2) > p > zp_eQ>1WU?;S8^$Gbic@O9Bpj}j&bRXhu{8mq#7OlH19Jm|78IAP+x}FT7Nz$Bc > ;i! > zSpJkeGG%cnK_ka^_A*n777nKWlM%foj)rab>{Y!zXnqOR3x1RV- > Hkg6e~c%ayO}6e > z-D(9Q7^=3DTW8tG-`!g%&A6k~`(iN#uj@)}Cv)k3>` zxDH>?hY3#t+@pKiOef^C4x?6d$FSzVi@Zc#FevC1x&+&6?he5vhx>o_#%JJlmx > yhi > zuC(RM2YNiE%to+WhL7(sv8&L`d)QQlsH9LFmJZ!9i5Xb!JU3)FVROw7^`1%7QEF > SC > zM^=3DkZYdDpnpozNsI71UF( *>o*46xhGpS8Y;XQ*+ > z!_Iu7yerslq0JwT9N0P@pTve{F@V7takLe%xI=3Dh=3D<3h)hl>g9NN3Ne=3DDYn&R_;$ > 3Q > zqTtg4+aH=3D{BK;#0=3DDiF2z2HIP&v0KU*QlJ8YlKwCUz-+H@cl%p?Z+-#KBcq!;rb9d > zjX2>|o+AZ!N+b0aw`lA^>@hO!H($1|3zh0ou=3DU$ttzytHD7WvWjS8|cF%<)ePqb > -| > zOx|zYa;20oYTSb^$f*puP6`{=3D@+R- > cN@=3D^fot|}fI?x0u9YeLWNC6h|j&PQWnYsC^ > zO@(4?u&0wGHD>)}f9@4y4H7&5a)v|Cv3J$+EAw7DS8(jeg^846!ROx3Fo_Tsv > $0gQ > z-8ayExc4kxrjbJosHXOA{_mM9azbn^p&#- > a+SMQ@Tv59b7zfSbF`J@FL3|c(?~Mw6 > zR$qM$LrL*<5Sb{nUA^}cEjQX>PDtTf&H=3DmIuSal7FJck;&=3D2WvBrgjF`9M=3DoItvaY > z8K^kuUK A4`jXw+Z5jRxVtBV%J&vH+QRLKJ}o! > zI;++vd>{!%+wh5p1TOs1XfaAqQt#N&Wv3)bY3`z?rBl0SNM{UHOCjw>*Bl>Z4Y > GYg > zi-_{4)CLA*f2_EP#Zn-|tAcPB?aP)f6z`RaP1`s7(6px=3D?>v+*2K+HHFEkw+<>InE > z8rB%F>eeWFb6c2A35{@@DFyZ > QYoK > zk;)5&CWfDIeK5C#KL}riyu6$<6F;Hj)uVPQ-~nOx!(t6Tuj$h6t<9;bWBf+@Ghs!M > zk^eY1)OHBj?W!o)Ed)pW*@9L!#y5M5Hw`{- > p3I!HZ(Q(cX|H6u > zAzp*GE|tO(`}+1|I^)t(Eihr0bm#1nN1QYAHKzSHBK8>Bv*tVHbuc~cg%^mjSi@ > wl > z_;!1XK_$>Z*cweMTnp6 `ds;ZHWg z_!d&4;KL)krKVbM_-%V@8o*8l!(tXYkLbeen)6M)u=3DGxUrs*hJa)e#)$Eprze*gX3 > zR8te}b~!Gxn &KLw+ > zF61^N^QP+7N$vda`+kb)HKyzBIc4ekv%gt0L*&DMbg}B}7Zv5f)ZsYWQDOhkB(~ > 6l > z=3D=3DpSlrsfT~2W{Zj- > #*>h*M!v6{Qwss+6pidN;yE7Pw|~oQ%Kh zHG3jkN@a18f1X%d_j!)So5b&5Pfd;_ngJ&b*>!Hb`++i%!Z~E9^34V7jnN2nvlf1( > zD_(Z0@z?c85CdF~w<_fOLU^-tS`HPN(z4oj^*3)uRpWxwkd{eJ$- > Ow%eFF$z!@J+- > zb)lh55pDiC=3DYD9#siX}?f~ltbr3JO$_#`P$8Rkz29uspIAsdbsZ`)|(K$Q?$Pa71R > zbT%WycXONGV>}a@^7z9!^L8UoU~omZ_gZr-gWX- > `hOL3Xr@NJ=3DToO zL}!}S<TbG9a2 > zoznr|E<9pplSL!*DqM$d|IjxT$FhaKGxdX5I@O}kE%i^&VZ;ETGf8}yOY)uS?1{a > Q > z^|C=3D#UD#&~KHcMo4|jT%x3qDG?QeHK!bKQ*5&gxty>x}N4j+NCeq3yAs{E?nC > )ZrQ > zukzv9qt h > zl2Xu5>iwSfHdFZ5cFlF?UA7)<^D$|9j5~V>Y3RREk)0#>lp}8S!gr5S_J%L^{XsyW > z%cqi7sM5pwY*r^oVhY)kF;=3D^O Wu8yz > zH+|jV4`bWI^L8v=3D4h`>lK|zu`F7- zw7h- > b*>IJdq19OTW?XY^GD#wCu5m^0(W}*cB5GrMycU7a0+BxUfhsLE;lY1k{$z*$ > z=3Dzlg}>gO`p- > 0g^DbUH$Wq8?zqBp20d!a01nkk#WOzBi?!VogblD+8K5blt3RFw7iB > z@QO~d@a9xlIIDM;g~{S+PHiV_Czbli+U- > }feE_&!ul@} > z7OUd1%{556U(Ex@Wya`yaBv^6n > c` > z(~=3D5}Hs0)7ICsYU*e?kS5ATh>%Nz76YL45gB{r2q-|Ef-lUuhQjhP>4g#j`eZL{aU > z*(6I1SaLpb4L0MXg_IC- > N)H|6qMH^~V?Z}WsHvY74cnlQgSb_aDAyTHsuc;Xs72C@ > zH8tBwwh-RR$0c_Efy#2fSz+zQo{D)->k`wG?8RMnf$ci2t_9S!ov;!eUr#x|K^JMe > zR%1_P`JBDqIt(m1YyY*@r(!l>faTk)U+h8V{itX~bTZtXQ;?#taSU&}Ftld=3DBmhg2 > z>|LB({lj6F$j5FfPK8T2!pu#@L;JsfGUW3L&0y?XA2eU#PsRmZxV*m`+P?Jb=3DoLg7 > z^^Ed4DW%_oJ)4a?I&&1f(}*{O3Dq?{d5WtAZuY8HZ{(va@E0%sxF?FP > Su5 > z)ab5C!KD#Kqj z|2u8zMRWbQPW)z#O#Y`dtmH8JGnqdDL`V(T*TkYk$?DKenZVWY1chr{1}#?^h > {bn+ > zzMkvwlT&g=3D>U;|>cmIgtDFDIHK+K>m+0Un`K- > @+a<=3D!C@tI}k;wK*4uNZt+ > z8xp=3D6ry|dbC{6A$XVU^!r|@t*8o )p^*Y(4Gdy1chpzMD=3De^fghwH@17p > z#Z>i*c_p}xH_3JEV-?-opWjuW>T)4rx9r~csvX17GmdD@PJF_IcekWRVg`@- > 1E7@! > z;D)|?9z~iC*K#$dSOIsMF!W5eONt*UzWYGI{8rn0KU8a3K1VWl%Rp=3DH2_fZcvU > ZA3 > ztewF760qW#RzsGzgh?*giF(Yce!2*s``rfoq>iBwr+)wn_Li;K0NiWo9>7l2Z4J13 > z#!A2h0xjphW+jrIEZ6T=3D4LsKX8ZI`tEVX@8eL_fxO$e}1^o$}Rl(!s^%~B%p(5wJq > z!T3S^`gEnBpn%)7?VlP1pyNj?0J4I}j70u@qq4g{R){-Dw4YLTRiZn)E7tAUy0|Y2 > zTiy40B|lL^##RaI5JTPyhR}y(&-ORjhI4+Soh!l4X_A{=3DS&?v;w6d#2KuFfy;J6NH > zBKe4`-#wpBt#PlE+D)4q`pEm66?GHj#C&%O^8oUM>^AsyI2SXk7hg- > 8vb&OJYEp}s > z5?FcUV_Q}tiqUB2uO~`fyxx5fdccVlVVdokep0y};UJh5NYdTui4x33e6;Q9Uj}Ox > zx$=3D?duaoWh=3Dm2bYTj1hp**5u}WOs`IuKdi~>_&uPE%k%0PQ~n1{HH1gyNjv|n > z{aj > zjcb4;eW(OV7+p3-nQ9QG)q;{|l_}}J-mXjWCUf5b{Ok~))qO!{n)8Cx9l!tbFdViT > zIj6xh4vrXLH5ZoI4$A#3e^WSsk1LPXX5jN5+c4E56A+*Ejs<}+&- > C_W&g}sGsC&Q* > z00P0G_7d*x7w0KwB12w*Q4Ic^Cy>Xuzpf#>H$U26Yq#gBBIyR@q=3D2M2W@1 > dFnWtdD > zNVk1t_y;mT- > k+?w2;11$`~)`P+7U`h2qDH?O_W`iWXQBqqRSl>4v?GfIM2(@vrq=3DF > zDjf9;- > DU6BkBSGThZ5h_k2=3Dn{S$o?~nMa)0z^8k&k|^KZtDqxy=3DL%=3D>bOA>YLuWqN > zwRjfyIWini{Z{Zc#TL&V- > &#~iq(M{;5Y zJt@W7jIRPCQ3K_vaG}Julv*hEM~5ph > -@ > z(^?a}xSr;*>~c(AHpQmc=3DU{t56Q1l?phjxu@0-{&T$IxAANwqg=3DtL- > f>WK&)%iKHW > z6XNLJoRXnQWC*=3Dj%$ic3B$rjID+ov5T%sk7^dXG=3De?w#LbVck8!QpNG*Gqg3yn > Kla > zzKcV{HtX!!3WKrl`Ohp-{h4Jx@4z(yop;e|*+VU%2_1tzA8H+2m&(73 z>iO&qe)e6BFmeV&<~6WB4cxX>`F*enMEYLjYC@zlJa{Ric$cPQF7q}UyuO|PpZ > w$e > zyi{Abf5L=3DPX*e6JNx=3D5Lech-G7+dL*zEd`*#$^P>QsI0Y-Bl-8U9|c<1)1Ep{*8c; > za+_9!J)*OdO@3=3D78DOkJ0B- > !5>uL`nfG7jIq1d5>X^l_27aXSeQ`K%O+;$)&0g%mO > zA~JC7t| Hx_2| > zU>hbJOizZJqNeA7QS(Hn##1)1wk9T5BKY*urFN7I4I!&m);xIa6!D=3DPWDkmM6R > 4Rr > zjk*Ep|7mZ0@dks=3D9kxy=3D1G zH!gjF^i^g67$K|Y8!NDcvPXqzS95a)S65dnFPVbHU%>zLl}xSx3{tO-xOd > zQ&7MLPDAVp9`7vz;XYzuR6zh&4P*h1n9H!KBb<=3Do{4HF%1dojBIVrY8x&|w6& > u@Wv > z0C+zs0E-43%0;hP0k{Nx0oXL49~a>mK+ydQmVvK3- > U2rX9A=3D^b2@4!NSpZK69Jd_v > zzk519ZBG{mS^o<`uG`bzf9T}KGk*a@DJedGk#(YIjs=3DoHobdep{(k$Py{1!q$f)*a > z6vm`Vw`uXP)aHTBzwp`dPG&AE7jusY_A05)*wp56- > 8|HN!VHDt?X!7%VRV8^)xU0D > z^|p5FO7zR8^SbZmA6e#3Y|$mRap}3O8;PJ4e- > h|F2yMD^>eWe)o2zdCq!a(n?9>#f > z_mJpnIVo?96z`{r+|ow~O&Q1<5%}*1`Zc)=3Dr6dqmrTcR@)^6*+`fyU^B7{~q`ZO} > F > z@|O^I*l45&W~^}ozxwrNGC)ytbNe|1WTvmbiWqqu>5E #qTm(t > zMZ%RUL8!a!Y2p%TzlgulNKCsLuvuq;- > }$nZN`2;&{?_Co$jHqWP>VWqdb7EZyG(6c > zAn=3DdDR?`I{ToFxr)7{2x!zz#L^ECP~G%fw5yn;GNHc|gftzMGi6&B<1lm&H;pRUr > a > zMX}`gm-bLL6ThX*#I^k||LNweGq26Dgssj_kteG!&g`TS0nvdcJi;;?sa8x5c4SgU > zPv!o?g%#~zy2q;fokrFYYW{6JE&k#&M^Bw?T2V`8y}CqUl&>7YZ2;5^L>+mJ9h< > pq > zzSrv*D5`8J%{$h0_N4}QTno*82#eCo3>esZgPvcE|APfjRJldrMC~C_RHCFk&eC > Mb > zst&>>8N(wmfHNuIl9jAdyHbzbx}4iqi3$&wr+c1cRFQC!kjR(#pa@rW?lSnxedn? > G > z#CBpH21G1wPTSYtt*?Df`5l8URy1hnvsbs}dN*jUE`t;07Qv!0L7o;A o > zcK5Oq7a(;$xaQ!)nc|T!B~f6b$<$5ga7+ zux6Q0k%bJO#W$~g`Qy#Rl_XsC@By^#e- > RM%C;1ods}_Dy6=3D&aE!DH&yZe}Pol_s&^ > ztt&=3DZ6- > Le_iVG=3Dmf=3Do>N6{nRl)z%TjTdGO5XEg!gcpE~TKj0O!=3DlWgMdK)w?4?D}1 > zz!nFuvzk^4sZ?@oZQb?%RWcZ1ks@U3ckBOk1O98?yk6vdU1W2PfO@tF&(G6 > Vm*0jD > zb1+gW(1Crf5xLvlJN6T=3DaDF}M*i5p(IG(nx_*93Y)s&oz z2pydY%10h4Ql|^g&a~- > aM2MC_k7`svOk&v*Vb>6){v6@o3lbKLF&m`XAJs > z z2S<^)%4bIg@_0z6COo`{PzQ;huUz0U!ok?8$zaaz|J}=3DD^(A-Mf > z<-7K<$G6OrXCLF&g-X%tW!lHIQa=3D)4iQy+A=3D=3Ds{za?3yC?9tXhIz^`&_(Vcq)xEpN > z!T6k7U7T)_AVOdM)zO1p#ERfil{ > Aw9 > zTY(F_%Anj@1LgIazbSM(bgK3>7?H^QlmyUCQ!h+6cu@B;O&mj1O|{R9kS*87C > C@?? > zRwRCuFno2oIYB@B%fkLR;t7mtCb34KikGcndJ6ax9xo-^_Ot?JdLg|1aj$ifpl{V^ > zb)w^n2VVV-t@VtC*A44_3k_Q_rgyv0P65ZZSOIZrkej;(3w{?>C- > R#Vcu^1XBSkb=3D > zbFl-UrM5pvtq4=3D7Zq|yyY<{VHDeQ6|c75U}r_o-- > &31)1TQ|Q%EAhuIXZd?<`#dHj > zbWs+n5+Ao;k>5(+>7SAh<4hFbi_CSriA&sz0gRop1- > k2y^JQ>&e<>!r+45@!bqRSD > zut3i~-=3DJV7F@E9SGWcBKr{t4+V(4bZ#J`?u(A8aaUjvhsES< z8@e(3tmNqVWq`wXV!qG Z2%3t^&^quv$J@hc< zTFWrH61TbJi1%fq_Dt7FoGss{v~RQf5Eq- > a`x7HIXwJ=3DfQf_ro@BbDbjW+5A=3D13?M > zstD+s|GWbG5A z|EhKWgPZ>Ur~ls#mVZrM=3Dwpi&py%kb3~~S{6JDBqmo71- > xIa;QyQ+A+=3Dch*huDSEC > cv#+HZ3T0=3D(U9vvFOET{$$f`i#(k8+G4Ze(OmjD0& >=20 > literal 0 > HcmV?d00001 >=20 > diff --git a/BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png > b/BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png > new file mode 100644 > index > 0000000000000000000000000000000000000000..6335653ece1605cd0c53bd2 > cc2bc21d57d213dae > GIT binary patch > literal 79906 > zcmce8g;$hcxV3?SBO)M!NGTEmGn9l#{pgk!hEC~Jx 9i > zX_)Vf;=3DT7T_+~AaOL#r+i6{2+oV_PN>6sM4je9pPT)03WBQ36S;Q}7}g$tKFuU`c} > zxjwDT5B|FNQbkJiLO~DZ3it<}nTUeOg$u=3DDkQ2iz;NST6(ukKAF5GIy{khm~mt}I{ > z0yaZNTtv- > PZ+!~CO2PEFeG41g%h9VUP97;8cawlCl`iUPR2Ek>T@=3DGWx=3DP?c7MH&Y > zSBgmL+qc~&- > 2%O(OFlj$dbRs>r7wH*JT_;RYP?4%`aSmCho`Ly&8B)!Pm zHq{J7Fe7>@C#~Hkw`&$vF}E@H7&cTm$`Dlur$M- > DQkUsiWm_>>F`39z{F@wQiMSjA > zh8txDpV9oGaPdQ^ZYeX&uqR`9ZN+CA55q9VU`Dbb^D(rxL}#^ > -oj > zN4Qp*J(-?sO0I5g1&e+|9j2qhes5;fB-Z?J6y4G$f#QS`{r!ZWGF$U~MZaye%g)XY > zl0(5(6oe)n+Nla$Hm7YDI>K$o%PeeX(Ma2$1> > z OF-( > ze(GY+PNz-|cTO$duNND)- > {Lhy zFfJ=3DAE4g9(8&CF^)52;vjN7WzbDtj$Lip^O-dx#OD{OQg(eub>QT=3D=3D~l6(hlr){Y{ > z0H^)?A-)z z|NEi4N?J7eB336yjj6EnN4iYpWX~u5)0H>hwJL2tX+- > r`IT%N?Xt3UUT~e3sOW%3^ > zGFpz*Y*Q03r}|H#=3D4kT#m<|IC5)Z)%m=3D}!e vG > zU*NtgxYa@u+o5SvJrdFTZ#d<*)I@OjORH~Lv%jm}OK?&AoUAToQOmkbLOz3 > dR&s7P > z(sv#iy0K^!=3DuAwMTP>kjbysIL+6(&Aq-u|TuRRVZ?sm+jANn`s#n$I(^UTllmapEt > z`+a7*-M`#w#C_t+=3Dk#>;@U71okFK3>fcP6f`iF2BCXUNu?nO_+%FJ+kQm- > &}GKx(< > zQ6_j6W&A%Ie47d$ztM1PTS)w1`t)!qsX(vZUEZj>ETQQl@%fTFWGY`_qF6=3D>$n- > t? > z!5Rh|kV18&cPs#c8h%a`cq8*odeoniu!Lb#(oN4zK$E^xYiH)NqBJZ9* > z#0UF2kY>>IW+PHjXv=3DQIRI$Ql;)iYRPoC%xABu)U$LOc~NzPwPxpgy&jK=3D#Q#V4 > Ni > zxWr ^% > z!&f*(jnmqX85E{Tv);WNoC#6nT_4TRxz~Yz&p~?l=3D}<|P%|ykx%>w($>HbW?i?4 > C=3D > zMxIuk&Az1P^T|{0=3DBzMmerNkNj{7XXiZUnsp;Qeqy)1jcR+- > DI^9d~!WoYX0A1$X% > z1(QEM(YEK)*}W|zBQs{+{+vJZ{LD>!p};4!kAJT%&A7`wA1$|vwwkKpR=3D- > ;cGp&30 > z?`-_vN=3DCl4Z9H|xR%n+Dppg*Wa?7EZmSy9Vf%A8X- > )f2nzV!Q^qU_$m?x3Qsmex3w > zfB37Yzs|WK+>%UNSXdx9J>K89E2pn5vmT3J4h}$}r^Ct47j- > #eg0l|9C)@T=3DsxzAk > zlV~Q@&s75%a*qQX8eMk(E$VeyPm*vW#p4&jK0)xYe7R^=3D)&~y^8>o(Nq&&E > NK9U!c > z$>Hgbcsu8bO1pi#Li$>`e#2z3dOp=3DLW4- > eKJY=3DbwrSyQ;ZQBGptmP=3DXzcCg2MbnmP > zDdg?>- > mDYVv$d6347N39pc2Xqnl5Q%tI#uWQ13{Jov&$cd9KcFCud&*&uL>aR`2d( > zYnUwZ- > 9O9clq zVkQuO;27zK(sb8{56>ULE^9V;x>b6gx?_2?Xcuu&Dj*<0+eY5o > 8 > zKdS5_ma)#(Dt)1l!(QW>_HT$pte-W7 > zyh`gaPV63n_Gi3Zg98&2Q?9l3GXjF}bF1`}EAyQBtUys|HNuK}$_51Ef>$d8YKD_P > zLL$x|O2|$4_>pUK%4O!a-6#4-alCdtcx~+{I=3Dl4qiOy*bg225Q!)=3DA~nShCGZDZIT > z`*O5L^inXLpWTzaUm%TSE z&QZ+jsW;Nfd9}{B%Yiw}R0|yDsrC!}2!(i_>e61Jc>LgvY4d+0WUHXQT8}X`i8}>{ > z^lSW|pVzqU{FwTEyxDkMi6Q0+6WqH?a<>ujtwZ(i #Ov6((sC#V > zYXUamEa^SlX5CJxoV0X|p|R5|^NxRORrCjuWel!e(cP#UT89gy!1HS&pFQpT @9C > zO$PyP14Jb!v3EDOuqMNCPn~kKtL#&- > x!> z!3rDC#xv5>tH7y`)PpFqu^6rITv)dqxHgax#D8`$uM2YTRG9GDGIk}8!|UjkM!mZ > e > zuk|Q9*5!@y$- > x$@%f=3D*ybzaX+g4pw;J8{dk82oHIQlNYG({Z6+2^jpF>5$`%tUF8^ > z1qmagoGc14>`hURl+gq zZNcR8*q+AXvV{RTw%VCc9U;L=3D51I|rn=3Df~G6w#Jh=3D?8JvF5NMV2& > ++Q{ > zQ2rhamCNMpkM7v{Q+@{7oagzgAoov&OHr>RdTj7*?)w=3Dnc10EZ=3DX;53yN>Sv > Y!Bo} > z7Ei|YlJWXz@!95E>(Qd7`h&S3R*=3Dx9BZ#fXN?LVYrkwPCw+t!@|2*1VnP;tC=3DL8 > P< > zV85AA*wyD<1C-lp_|@)_>*?>2DGIx~Sq0~BIqI1B7R_QKB(Fsib_qOJ*WMK- > ZRAE+ > z*xt4p*!SU|NhKAQxbwBmrpho5-EIvc+h?YgjbdIzt_A`lslG z>lomNI( zPGgnRB;6Ws5i?kv;0Hth&wlHxG~K3~@jiwtu+(=3DV0j$IPULC@4oA- > UumgF$+yN^{w > zzFPg(sa~Kn26D<*?5Eo~ur3sQ4GPy<=3D;2$U9I0`#!p@xZD)3G%C^&+>YsMwtjrR > {j > z?S)TwBe8aV#^9J{@q=3D?k#TfpYBC(GUF0- > 9#FB%x& zQ zE!@fJJgnvp+i85N1l+c(aOeh0wUp>Ohpz$RpdB%7X`- > NqJL%nZt${4%ha;&H5Zv^+ > zM|9?U1ow69Vmn&`NG|jH7MpYuM+^ISGqA_Kdnf Yhjbc; > z3H~4^A(=3D8u^7?Ulw3OuI+!{!_gNl1wY$XMpdkRUWw|pA+F6m_78++=3DqIT9n_R > x-k$ > zW;IeUU1%|hC^6~8uImxwJ$}(`kBc5JjGgrUHvse+!- > _R)o%4NaDc$tF4>RNx6 z_u{eq*yhj&u!kv|tusE@pT7&2`%?UPMH~RAaP@i|Q)h4}?TR)U%2D5_+v#};@ > Voub > ziOT&m&kWh9y49czxoCHv7t?ia1=3D8WP8{Ln4UnO~N2kd)@Y=3DPWR6C51O&GX > #p{~VSr > zIGh?@^4oXR^th~syA_gzrm&xU(nROiOMCa6?_Z3(i%ZHCiW!PmYd@MyJK(L2 > 6z*3T > znDTXAA=3DCMO`tpZNyl$;?_5O^{jMeH`^- > D9Xi=3DW|_*B95jIA_{cNB_^^LFA&C%dNUT > zGM)V_!6P^ouueED`?xFlZ})Fe#YaSx7`MD9`ty7K{Lv?;CNf+9`+L%|yn}=3DJu+s@s > zd}36Ej{8c6yn*L(2 znrn^yQuT!b{x7FEt7fZYOo9Y>GVuaG(D(8+PPgJFmGS>gI?nC@N!KL#oQ*Zwpp > utI > ziyxBQO@qOH{~drRzzPW`NdhK_tw6kaeKj#N(Vdy(Zm)Y9-M=3DKg- > y}h%yHH_0Ha8 zmkwYVX0bQP$3Nt2M#_VK@HJQV > 6Mc?h > z;bk@xJT9H|(M6u=3Djr8IFgMqQfjdU3Q5DMNsyA=3D9}&+@Tzfut;Lcx~#_%YV1&e > TQ%X > zvu4rvXYhvsb<~3H>~6&!D%}+1=3DSw-1a=3D?6i_UY+iWj`@Kj3!$>7fN!MO^f*M- > G2{M > znpqC#t)SrAKW3iEJv# Jp > zum8=3D!r+B_~0&G%_%VRj-TC~2mL- > ot07njmvdGDQXScyh?WMt%s_phdFXEx;vA6W{j > z- > gE{f!7Zkx{|%y7oU!WRv^MON`#gW!E}y>k^kk=3D(RWVUuVttf%UsB{e&F 0 > z*8iB%lXD)gb1StPNXH){6N8iA{5PL @ > z6wP@W%D- > XH@4WsCAk zteS4mvB^nnz7P64T8D^S@ov6y+B*%V`v_1Mim*O>=3D!YMSn(jX@pTotTk8$n{Y > ugQ} > zM8N()%W_L~2Da%^cIAH-<- > an}($Ydz@3FsU*SKh3v?dM0aBN+S5z|j9!}DX{v*wES > z%n=3DVDwmtYYdu9Xb08m}lrl+SnIm=3DVHN@<+$P>H- > AhOC|1BQKucrND=3DU^;)joOarT+ > zpjRywhQB_I`TR%_1;M7)r$@i1N=3Dz{)+Kzlm0GHs(@)s-8ymFk^)c%duzJ;nL)3Nu} > z2IY&773=3DY1%Wsb~a@4YQo!5oR(jD&qtA{*>O7lJwEdtDQqWx- > &f`Khm=3D*XOz?S z%8w-r#pfF@&BNC- > fGHU+S&NWkbpYk(uHBHSODqH1vPTnkqwK_#+{kTepx?22QC!aZ > ze1G3lUS(j_E6ppDuP7_wDubDi7Nuy{xfc6iSPlKI5xy9^9tph+kAiDxUPYh~2dDP > W > z5ibx>&9gF31Gg`Zz8UWpr6CGx=3DkqjS%h8@LQgUee`Wuewj=3DGeo^ U)1gME > zXaJ!`N#R-_I|I&8Xv!@4n42{d+i9k`NVqy9!d<&Y>qx6jy8@9lnQS!Ah&e{FqH^Jk > z+G@oQ{%)#6*o#jv2ThyVA4gZ^E@BcXt;nB)EVe(Sp?_HUYYQMQg#y*gXRO@Z > QJwxv > zuocSW`VJeuCUgOvyk>WKaM`4(p;1;@#^;QX41Xj(VUB)G- > @!x)8v9!cbq!92IM|FT > z&oVkMM~v>tL8r^|ig8QXnPy<+WU)Og0x)S~%q+?2k5#YCw*&M&l@YBcJ9MR > e z#s8EQ`nyHTA(&SvEuo6CA`?xt3cOvjr<9#XQCp}|zE~;0xD}1pZVw@$P}D3aRw{ > P5 > zKmX|O;)u-!R(k8y7ThsStjf~GlU9hKJj7#OfHtwiLVE;v*}tggYejWJLZ=3DX@X5|6L > zPm3OqD5xB4mnPee$$U(=3Dggyz`D7DxYf+B;ToCZLTtYBqtkE7W+wm&QyVPsHg > uoJJp > zmLC4aDEoRn8Jd)A@`QRL(%tsEkJ*06biGF{$V`WohsFLsGJqR8fm&8xug;38`C^ > AC > z48OXUbG)RPvu!&aiJy5n9HY|e7>bl(I@>Tq*|)a6fRIuMYcJPteMELfs=3D^YkT~n1f > z_k@T=3DOGH*xvM-(DineyiHj^h21_Ej>E4GFMBVPAib1X`jr*CZz)aEWxEE+H7- > rVrF > zg3F-R(c*YRq-@%swU!k`8- > wHF@=3DBC9nR%AOmAld6S56gbv`5FI^e7+0gW&R(9R2;h > z+eM4S5TAxR2~vu`Z;r^%s&bt`6WF_EOFQpLKAG571vnnp^O~BhetF9DdighqTs > 08Z > zsxS0(bVYvBY;srh%x@VvzV{|q^JKr+${DE$*FfBIG4c%{uJAOS%xZC+L_cGO38rw > Y > zNP- > 2+C;q~A7BE?7V_L3aw2A+xz+V@KeC6ZSWYEm2>d5}YJZj@7>zhq4`|+3a%KM7 > 5 > zyIk~CLY`Gt({ z;AhipC=3DV`GDsIAVx G > zuc7T&i77bNpOXN{oie>K7zdpc3NG^x#^mw^I@Ox2Xz`{D6qj6I(O}0Zv@LcT+K9 > +X > zHi(~CUo~xNk(7XtFswUo#~|q$)vT3L(CJr6DH=3D6L;fn}}x^Ouh34@uP+tib`uj|@S > z*|Qa^FD(+(a<_{XEj=3D(*V4rJ0E5z3TgwgUfp0_uN=3DY1XypbMWJGz(%+b~XS&aRJ > pa > zZ1Ap$g(d!6OY6p{b9up{6NCg`ZR*6taPIZugRu}(x|5qo__`yydgSYN6p|tRF$@Ds > zh#!Q*Bv!1e(JH+vovDEYe`XSXSE{wu16t9&AS2*)lUCiKSQsW_5PPxKD9@N;U^rj > 9 > z7Bp_FO))4p5_~>W&`X^zL4hW16Su`c3n|O 7hE2 > z60eag06*Wp^(w}0d91&4JHRx;Gzi&A%giH*h~aXd7L8P9=3D83s&n5*}9LjsI;84U > Qp > zDWHU5@;ZGVIPUcs`wHPx!NdSQCL?1~Y2mh=3D9Ci9RK14vy*@6p?ptI4FWc$Pu > ^kAGC > zv1hew6P1N)0Lk)$79`cs{XMKA;WgX6c&hC &qx > zu6PAe_- > |zhU5!y8VPMz1D%RTO2*DT~m@(AVXvSRNYZg_PdH(zOZ_6g!+VRceWy}o- > zDVg;W`B&bT@-K>1Zxs4{PLqFR&8l6oUf}+F)lGqGvk~h(lB>bSYybVf{Rz9qYVyY~ > zsz6m%JCv(Y`+IG~gM^CZd7k~wQm;!SgTi*4U5R*xcBSn`#f0rkT=3DScM_AQA{IIV> > 9 > zsE n4ylktVn@bolyQxU*nt3*XxtF_6#`i+ZTo{ps1LlPb{Pq > z>&eY;uWHw;6myKDiKEEiyjq%~Vn{+c?~+`s|KF5Hc|mq{>{e8K)eIaC- > =3D%ubEfAZ@ > zh+c#;kRNF+Fa0u8Q5l#4O?9isoSe=3D|g5ivci9YB;i<;QP#0GkLdQ($TQMC0j|9T9^ > zDB7WR#4!=3D+O__~yZQaVa^iTjHex+9l;>Du5N~9J{E|+GBc(Om%6UC6ysFgKfAR > 5My > zy8eOl3%Xh?QaLW>7t0Z}^s)JP^ip?hbg(2+{Ozt5@0va > dA > z+1cOX*no_Gc~3?K@x?^qLk{H4JL9eaa(9!rE`%xLzCSW{3*KKnNmzud8T2jrF- > L~M > z)EN7@K2Ev1>KenXFHbZNw2AKYr9v4lrj^h9EuE$B+ozp{A|?GNJ-5EaW- > e{py@Z9m > zJyX7QS$n#oROu&LL6|@xskrYY?L8JR^Rvz#2Ciz;flVezJ(s3X%d zn=3D7pj6^nO+{)Lu3zrdg{I2T9y+?=3DIcow^QXa5eOvfb6hA_SI9)QMd^TIc2lVyY&G% > zeWw#;F3H{74`sL>k9ew;Wj|NnRsdIfs$AT}ng0Nlh>C~rE+nV=3DHx}kl|8bN6D<{y1 > zSJSa?&I$~=3DoQ5B~- > fVK!OKkM#J0X(Ym}Ccrs2frUBm78WH@KQLyM;6%0c!ZfI>pGi > z+_V-c^T`+LZm(m3HdAxF3RFoty*NC{rCYtKmT^7&0^Ek`#!&s<+X > zhX_Q8?o!RR2|rhMQ0@^`zaglxQCVj;Ti@l7tP&VVP{DcDv>mwk97K3}n6(DQPj! > |* > z359qmtLnSt`^<4`3z^m{+W&Pm_2WtQ7<2kj92!DVx<&I4QpheQd0$*0d5bZU > M9o^q > zVwTcG)SsFb%)GjI5G~#t4Lu4dnqnal;z}E$%lKo#c=3Dt%j7_}ZLWX`?Gtk6Kbdi-*k > z%a@1`=3DJ0d~y_Ffes)Bih@d6IfV!U_}q?}*Bgc|z>nzjp&`$zX6?e7B#+-=3Dz}?&d5y > zV^&Zq(0gHR7`hLC2fi~Q&|qh{ tq > z;_>a@t{75O4~h^bto0c&j6jo8?2@m%sBT3&H? wS# > zI<>o`eWH(X${{`PA}so8yC~B4gb+8u1Ovm8IXy_^f1b~aE_WM2 u$ > zlhQnZ^^0fynII*~9a>>#vh+qDHzqY0lcz$@>kH?kXd_t;0XVH=3DKYXDver6s^P1Z^ > Q > zOGBoveQ4;cdl1f&#rq+s2^dT}+qR!B`_E|WB)le!h$C&ywX<5}RiB#J4xzX&?{9I{ > znJ^Mo(3o~Z5zEmIl}K>B98srfMrmcpfRJ}6XOs)f7{2} z-axq_f9i{XmjcRX9ipvQcyFocWRdId4dO}eb{6Sar=3D%MAnnq?6F5ZJ^hjpbGI{K|b > zDr&ttz}21$)LCN8yi=3D!mD-{_)Q1Q-`GyU~nfNXSEF+{P#(-L1pr!QHNl#CA?N`J_d > zY)qAb1s*Bhq(P&Nq1^K+H5ws?psdkkqUCBRv)+>iW0>a)cp1q4C3oT=3Dji%L@fi > GxV > zW+Xgk^iMPoaLhyqRlH?kt-@DrmFNL8T+gy}8|6f!Ln%q%VF*iYx;2jKrj|BSR49~z > z?VV;!_jL_mlUw;2nHl-WE^=3Dp;e!6_zHcEdnEvPckAR7KOXrRc?A|A@XZbZ15sS4-% > zY~q J > z4($FMjWpgT+ryTUW;DrME > D > z(#&m&b5s<7O1UZ{Kju&k*uLgLDfhp{cD_*4DS9bdHTTA8o~r|_*WUcnLvJOo{( > zWamv=3D)R?K?)tc34BJvgJy~5|keY}2c$-(&i-6uA&cC&)Ab|gZ=3D*`o$V^OO+nb!h2% > z)Lvj$c4+Fkp4W;UOAQP%7{HVCG+GLlf=3D@o$S@21~T2*kM46g5wCfl{uqx7Ih7 > D~m% > zL&OE62ALl<;8d_^t@9$q@j?VVubxmtC%B^TJ8PlD8yS+jTOkxvHPATup3=3DTdU > wS z*m`QXLHPG%qqM4w)J1s=3D6%>`)H~)6)VO{1*X;b 8Q > zoJ6nV;1DH2O4gK#e-q!ByfX^wZjx7$SLSKnKit;;VA6P=3D7J`wF`FJ!A_KpWcFPMZO > zMwdZwln+VoRSwe{X2dx%M_3@F!u+JWWKvYaOI=3D5tOu?XIBdg}gfMX;Q9y- > ?gmho5+ > zre R8v5Z3OKYwF7I{995>T{A1a4Im!)_ > zGw1@^*}x#Rpg*IY^^v7TBI)sKVK5EX7L_U5{- > D4RV>p@CxRzdtr$ww~ijkk?QwBco > zq9W3DF=3D&p%`5r2R@SXTY#i1B2dilS&wqo@(aYisnU!#Cd3luMWa %Fv%A > zz+26_sGfDcQ**{L#qCkllgA)SDCLW&_qbw?p>h5^CG6xX U< > zAm1Gc*g8zSF*pR(irR<49t;v#Tc;YO*8_cnV$a8<0-}=3DOWCI_3x@9 > zfonPcX(^0_1_rr9nOV)30otU8s_V4e0yCVIF8fuMIDNxY=3D$A?2%T+fBKXEqAGizh > w > z+7HM?CLI>riaMhdPO};$e#jMQjuV5X`3;Xoq|9y9{ixz)_dikAKfeL_Q^~DFvbA*) > z+7(1!VSY)hgjm6gru4)De^Yw52>I> z1`ul0pMqd7ATVeUei>$+CSBU_0vh$UomIg=3D+S#D NWo > zRyGWbp+(<{%Zlr)4T+5dkuMkRFr1J`0~&4Wq9Sf<1v$hy8feL_GRVHJ*y?IIJ_$T! > z6l`UuEj^s;Xx(hgIt8C@!b#6srtgMS3r~es%y$E~8*<9jl{&_T > z(Uj`exX93Qk(t$1R<^a(62k_yWj>%~+7cnT8=3D)Ej+r^lhrRcZOLTa^Ml_wbWM>>s > B > zjXcV6i@`H?8wf#BLv!4+`eIB|HgD7#DO7;K=3Dv=3D4(s)1QgRT^B>4!`?f>lG3*fuYp7 > zH&d6@9Eg0ld>@4Wx1$&*BxDi$V$5i^^CCOVf509yY6r%vnOI`4V9azQ> > TQCm > znzg- > `v5}vTBK_@1)4w4$;F7>aQ+~CEh5TX=3DW=3DE+N=3DOP){=3D?1nDJ>_+FvjI<&6!a0J > zIkAk(wATQFU}lxojL8BaMgpS*sZF-`Rz4I9V?=3D%SG)n1&)h9lyptPCw#8D5zuUi=3Dy > z3UXNWxzRdhORV)uklZDvvu^KHbri})3lbh_-b3uV(fj)ahIFAy;rX!LzT{03ImN8e > zQIlCu3d0nAh+KR@X%Q7WIDP^g2$bdlE6$*uyqO339a#>ECZ9p9piN9K)yC&n > >SOcM > zWdeB>nlFGDF^e&|QV_mgqTVffrR_J04CV))hdqYN!E}TXt1T^UX;A75eUWMZ > %4x)R > z7{p^^M=3Dc-_B5U$HL*E$}gq~JRo2*a>-i&F{v?G5}-7DJqPTCn{7Nz>p0rZlI4~7mz > zAqwD g`?g > zwf9v4a r > zz}AFrY5fnPpAb{kr$WScJMPN|bBSgN_d9*RBB>ZZYrAWkUyrjS>XebURx440zbZ > !t > zKu`%v#UjGdP5}RUm$R?5wOo^Xo;CW&BxOJvI1}AxWd{+ll3y*Y_Ml!m+Gf2b3l > 8e% > z0p&vHo*1eh>KyZ3<_YhIT} (n > zqdFZ^zA5xu=3D|Al^X*Gz3&M*4+8MLzj5Otl7rUATnEyNXXg_^g6O&d%zkly4j+O > pc> > zZ{GY!A%)4S=3D@_Q)Ew`hD;p@;gXSAC2$%|Pn#h3jQa3>hd(9kG!^Ac`reOm_;>| > 3uW > zT6>qUJERP#@~MF>&}=3D23m0MYk=3D9oa6*y&?TQ4e9XBF%5{1an6#q5vwo<2{U > yAaR~g > zku^pl=3D~dP2`Zfc98X!v015wP- > Kkh2s!r>s&dc~?9Atl$^DgmNi$1HBXmr*f&_FMdr > zS}FPBI>Qt#v7|I&f|w7+6T|LMhFR(gPZJR6U%Hng14B5Vae)oy)t!GTm!6jma=3D0j > 9 > zLeXHIU`%e3GWSNsoSC&uFy4d|7=3DplTX=3DLWNUGikEq@3VR(4k}0e&xs5|L%so > Gf%c@ > zb=3D!6sQt#eFoN)yQGNjH1hA_gdAsDf6W7r>;4R40p_8gWYZ7w002U=3D1Ykr6{625 > bew > z5lpo{( zeYXe~R`&ujeXK7^H?s~`otdZ1i7H;9uLUuB0ei+4mQjtgVOv5}H64Eu&GvQ(lFpN > ^ > zV0UwQgPbN;Guf>yI(-UWG*>!>ekQa#y=3D- > Y=3DI$JBF{x__)9E*zZEmcCFhV9%@A|;C; > z0dw89i-PlNE_2_}Gun({RXyoK4>xl)*3Y2nrd=3DWRmFCp4t!*?=3D_s%- > & > z>Q=3D`%q>K=3D=3D&;cust-ujXyto77S~3Y)B?QSztp>&i0t~H3+tROMXrj^}n_te8uFdLV > z{!UtA!mJA!M)R(65XcC`dC$- > SRNn%x#w=3Dn@4gI+)*k4@w zm+YBYS<9AJ5*N4>4Bmrl0j=3DT6KE~I2 jc > z8wBnya9kTJod#p*fv_!4uw}9^R1yEvv%5%EP?gNL#Dm!fwk1tx0k- > w~FY#E${v@rK > zj^w&CU5M$v1dMhoc=3DPe@#}uP^W;rv+ME!R1yFh|zwpy5)b>HS1AB4m=3D1lEp- > fr0oM > zeqF9y$w4I3?w- > jOS8B0@AS>Mf*jUHqvLbpG#ViTBxBFrUFr1!2 > z^$5CU<_RP!OO5Ai#fe1<_NJHFMp1$x9~#~AU?u|>4=3DA?i- > Z&+z>GTbGWFZ3~;mLh? > z@1%zfH2gC`HNg+t{hF*VsSxyjohp)o8w97Pi0R5-$^L-^_g0=3DlFv zNC@J(cP0PDfl;11<<5z|kq0M)$Sne%U#RVxQ}+e!*o1- > (p9OWCabzpEq45mFFoK|H > z0cQWO%Ls(mbz&|w`uOKFOf2+_T2A>7hAN=3D}5IJ;*0;2dGbY82Yq?NuF7fB+- > OY}tK > zSmcBu3Iv_G&;rOIjMniNKn~Fe>SFfdCc0TJj3f?nZyHU3Tne0)n!+8l7CJ7b400)a > zx`9|8?Qw5=3D2+#AUJ9%j#hYwLKtokB8*v&^uW~QB0rD>7EFGxZ@t`4HrKFKCa+ > |BJf > zG5ZvowT*DLLu~hP#6IJu>dzIn2&d7&2*yq-_zo-{zTWc%6}gM|{b`5lyb8()nn?Zf > zRLRdQ{Bm>p)e&}7RDxB?y?Ih_EGk#ZzoCEIrXn{jcXJGxZV2*(R=3DjQWcl26#+W9+ > T > z)0vi*_}W40^G-Cui4C+Nx!*S~=3Dz4~C5G>Ckoe{5e;jLgxEcepN9V=3D>!_j|EOid<)u > z9IU(IrSFY5BeswivM+5J%-(}mcvBi24si~mkArbb_~r3o|KZf^n7UyE > z(o~T~E3u+4{evDWDXb=3Dicubh#!78GlSim}^?i9EWPuKJqlKEu%;d)omkmzGEh9{ > $e > z+uCw=3D5W$NDrS@s9bdA7QBFSNkHPhQ~kZW2$_9r)H- > 8)+593lMa!6m;Ey~fbc;@zbu > z{ph_yu&l)xHG{pQ8yE@{2Ou8pKuDR(vH<}D$LK2neawv%e`-g- > `zSeHh*X@cobg_J > zM@X~befnF27hEOr0@@Gnt7WDAmCU4?5q+O2r $azMlS zx!KXp7x`dPNxf7&$Bm>O1`Nj}JP>jg%^kPu0I*Z+Q^ik@A~V!Zdv|wt>A}7JgmST > w > zgv& zs-U`Us6cmOONbTRthS}6)7j>k%S+>*WS|_OpLU2hW4;- > q=3DU!pbpr~8VdgFOFb5t^9 > zt5=3D6||Fn{8Hyb7dkJn2J6qAH(zkl)F0NjRGjVIed`_v0Qhrg3W{3Zd9X9-6O1aqUt > zUHVpmHvcr0LkkfrAe>sQH)8PUNWH_fmrGmTUL_ QDz?mSX5 > z@QUR$on*>PnA0jVpQU#FesLd=3DZmPqCjyk*mqh;EOZD+r4v*EZ0cP~@_dsus;l > QHSl > z9~v;YJ (N|kzu4PE}n|_xPKp!B{Zs(Dvmz__!FC# > znpzFfZ}Dxx?bU|8%}m48*95*nudP zW-W-#4TJ>Xbzx$;)+`N2{62xp6K4$_2c*Ypc4i$#O5@oP<|iPa_%Kq{5K!<`In1~H > zVI4Rv_ca}hYLuBPPy1QH*XI>qa_^mx1u(+)tGc#Pi-q65skv-- > GI~UA3cBywH@_oz > z8AQgmHzVP125u5IaGVYtGpcqGrLFVnsZ+v~{1&x|N^uzSYRcr7fR;#OIE>U#)KD > Gp > zZKwE_66VJN`&HpiDEZ?yT!Y((jH(7lf8cxh!!`1xS70X?$9 zob^;pobL6bPAi+kriMJYG41TpTH$^o1E2x0$T7h7{P9B$ca}U9b2mV18b^PL=3DC# > v9 > zrQ4J2CugM6=3DO#&SeLSRuC^js6e?98lfw>>S_?S9XT190Jt2__4Z3I9=3Dx^9&rk?s`S > zIN2`sd^+Q&Keh%;uz0of2*1^wv@oG@dPc_53G{8u5HzY y8jBnk?{Kg zx$KRDt@$ayUORP>+0&|a%sAfZ6`sm1Y*@X=3DZMoq3+CZ3)z%8~r`KeOK4f^M# > zu`w}KSvu8@8(l1gyE)e~0c~NpP=3DB*gu7r@}UJw+%&77Ha_cJ;TTrk3fQ)FXIqt(Pe > z1KKm)8`qky$Z0nLH=3DCiHC_p9AM|E4%urhFYAIx4W6o_W*+k33?; Dot > zDSsY+i*OE45D$1N_-K^Ehf2{q>#YI#^Jns!>%G8->T_6733r&)-tXUE^B;?g$~2}e > zevN-|c)$k`b-%BU6mBN^oVoEjt*T*lZzBZ$OJ1D^v>9X0@cr-h5P};PfpB7o>k|p# > zSSG;G`JF>B}3o<~wxr+c<8k?XwA!6|33f)HuVS_Y3SV!u3mG`C8>U9>5t > V > zan#wcweJW(9B>ig|5-Muqos9^0RI}|p5`rQDUz- > o1WNRw7&EKpp!}70%&c^b4h z98Um8K+NwdL0!O`JZ8CGOM$339-FvI=3DPD2uJOk3~2- > d=3DLwZv12NG{0Z$pcQ zma`U)Dl~izf~nUW^Z{I;q8CZ~-|s-=3D>X!Q-5QmskD?C29B1Pr&(c@s&e+uwi>ijiC > zTHf9`oz%39WmKfj;e3aP(=3DsmrmwIDab;yFRR?h^s#P{1M?5Y5>?SQ7Ysa 6I > zZ@S{?o2GF!guqJs7M>vd5prqM+a~3b7+t3FXCZbD4hN0W7hfj ^z > z1*3N@^0k`1MM#4;WF)(St(8zr$prlXe;lcqfacV$5Lfn1E4Solj?<3 > z<50dfE9+W7#lo(K- > r~Aq>Lw>Edt+0C+0E@XAd?}2bp zs *< zk(8oIlvEzy;GH%-mbOh@et4f)EfaB-3`EBE5Z>u!2F1knq_6z}-rVwE?y%~_n0~M} > zd*+la)D`#m& > MC4 > zfo MC > zBsV?s;@@8AB=3DIJ8Dv_X*4%9UNQVCQLz5C9m@9EQ|!ko!;UlDCK!ogMedMYh > 39ZZ}0 > zLt^3vj&#lIv1g544cb!L > zW925f_0wT6u0 s0rJQC*k z>GYw>U{=3D- > 7!D~ag(UpVGU`CutKj{I#v)AK;DSe3R7A;YmOQAL_XpHs%Fl6_8M@O5t > zyRK2YegQnEq`<6R*eVe{t>cH$h%F2th}Qx>9Z!zj5aAtt`CRqfw&+Ux#M1ZTH+X > NW > z@-a3Zt+HTYAvRq1OxF00FM+hnW-sM%U!3- > Je!ymF9!@&)jg5>?2{8NsyKmZu0v4vt > z<;E5+O5b-(PEI}@a+;%_ds50((SI|B`sLm@Ld%W(nkXoAHZNiFaP- > =3DVGbTMH6m~9% > zt>)t8tF9CF^q_1no$nh?RT1ixnPeCEkdRPU43Nn5t*2Y>oSMMJ;1Xog1R`iA?nr$ > 4 > zLC9ci;c>6PJWjEa6uV`(Hk_}QrduIdInVw@ls-oKS&kVw270i?>6=3D6@nEFRC5Es`~ > zQosn`H6|enuQ$-HDxZAPs!|x_C(+soGWj!Odf7uwlDr>pB1mp%>Ggi%uN*- > LqTF>U > z^KiUZAYXWSxqM(C(C=3DZy< QT_A>V($*jj6IDIBY+Iu?qUCCk?vNg^sTCIB{4rG > zv4BaAwGLJ+Y&gnc(JZ>Z*+d9y<$FR)yDhw)vn3ChY 4|~9MCtID_85vYp > zfQ~<^;wF=3DBO&RVk- > REi>Z=3Dz>ur4Qig^YHV0f@f1c3bz(5?@cJ}d5XbN1cf2<6W}hq > znLua---+Xz3|)MY_1ZLWIt7;*uS#%V`pE~{RtYo_4<^mM(=3D#qzlNA>i<-JnxxY)Hg > z?YX0XeW;3- > 48A+t6~)rtY8+##sGT23$?7f`oT}rDW9Y^*8d_JzfqnD%(7@%{N!n>i > z0GC^F5jm_l=3D>=3DXm1qhwZ$9ClsR@?No0tp})1qn$5Qowev&_zMlP~kJzX+ILU

vY2 > zLzt73U0>b~PQ@Y2Z0dr$iO$t3XC@=3Du>tbv6CF!KiW24aplz3i?0Xl4O W=3Dj > zD`82xT*wuA7F`oW>wwI?&MqJ2D7SVhm#VjK@!~x6=3DBc>D^TMs&1OR`y)_re > %CCQ z7Jz^%0Mi@^(s|XmZza5!7MM%jVqRMl!k)auK2+Iz0l1GFqsC#2a~zW@5tko1# > &l`5 > z;aJ3LXKcrNv;H(CLmZn;wkW(?$sLBN+eU7{w7A`hY2=3DNHK9y1xIziQqq@TOd2?| > b4 > zeAe6xnN~7OaFBE*_M5sSAwG8lBUw|bJDZv0z3Ri&btVt!*vdcb+HK!wuwPSIxE > )m# > zoGdd9^a>}B<@c8A_orXNTuP0l%$ykvC2pyEVK`AzEzQqo5r#lsv9UK{w{%)LEV> > xj > zkzz!A)bQ=3DwH(eY^@_E;J)}16g^T~ZRyY3pc$UN79D1hZ>wYd@se$8xmxj$4g$o > b|> > zWma}FW#A;Lw=3D0@$li^vyauUKZd3270iOIsBsyV(sjmhy>XmNMC$RfzECy(WB > wX}s$ > z=3DmOMGb(OU&;Y- > sUK0FPr3ZcV=3D$g@~_(0)|#@lQ@ciBifFg=3D@OF0YSt&9Kiwm=3D%XJu > zs01{U|GUUa9j5(FP3e|o;36_rBFKH>4!FOnOmArskGsLM6`IrGn|5SDmobV FK > z?gHT90>&^rU}E?36u)(KNHK{2)$;cwWyhkd>q6Td58VhGY?SpM5d~Nb!Gmn87 > omSx > z!Ny9rT5()Lz&Pif*jiwE)8Shq1ekIZat{{zim7L2$PEiJQe8|W_QLR&Dzm*+1}B@v > zR7D!uzL`O%{_FH*HjSMGwC=3D( 8~w5t8O19tXVdlKcX > zJ?2W|nr;=3Dp%dirfa(FIqBtz`Yh~{D-sj%1B+l0IEeW5+>$MN)Se|>K{L(uvAF>^X| > zl+GW;(D(DBX+7F|T#|;el$&uR72%)-`&VnjA^S6u7&l4-u#2Ll>9)JJeX5yx&Eg+s > zh~hpc9`g0kdWZ~;5CLet+f_(!1)xnEj@rhkH2Gh^Iwl*Xu3PhJtCza-27uAu?{)Oh > zEjfOW*qcoR8W&qED2Gl zt6X2*qOuIfe(yA?WpMOHVZW&ZOmrGRQGI$R&@9%`#vcF@ILOpZll > Tnbb(?( > z=3DQ5+#G8=3D6PBO@anU0#Y$xJmw4saY+vdd@Fu&q2{7I8k_(#w4fj?>Rt#*%98L > W5JPe > zW*L<-?Y~Gki#3;o*wt;l^J$ZQj-!Cq0^+|L;hAOsURw!c7-xUpjh6K$0<~(#MOVTG > zgOoW{y2q#k0z>>#hTEl5jozo`*7dESJFU}1WJCc*0*78dveH7gD|92)i*2W#x||& > @ > z?JS1C^G9h_>0n8V(Lj$?4U{E^X2B- > qIE_*Sz1%jn=3DQsXUTC6Gy5F>qZp6=3D4K#%ySa > zau~8ijC}&we;;)svo~67>|YDimsMAvmE741t$8o~F~<}!0jMh2htB|qRyj>)W3sw > ( > zY^$M(f1CD!4Fn3S@`Lx%c?j-wKlXIfw#Z(katl}D`~4b77<3iAOUdPASgXEYn)Vac > z8sIyi{T#fwiOa~ss&uv8pd!Q7kGpSI7pUBb!XLGeRracvM8CvUm<@HOrc7- > Yq$gRd > z4v+h#XnyzJ?tBJVwvJA#-)035z-~!IF8K%Oc)c3a6l@P%0*!Tl67fA > z8K_Ar&HHJwu<-S7^&Tz- > z8H8r=3DLK5Y{?CRW{h&nrt{&1@MNGZuj+q5w15E3DQ{Qdr > z1dkeke{IiB4$sJG$9{ri{V!CsA%7u^hBcisU6AA zb6N_RK%k8JD$Gy!N6JOzbFx);O@Bx7KXJOibi`GC*IDG5RNTTxK!1HX8)M8D=3Dlh > |` > zN#4k4j`m)iY!Be9XXffR3T-y^!gA2>?M8|=3Dyg?{C8+*WK2(ZTNr|ONH1b!{W(?Hd> > zeGe|z<97VBQWr!aOa3!PJPviYdxQ8wi2GkiqtP#g%M p8T > zP_S!2F?(e8Zs8+fmk)$^<%WoS>Qj=3DYOm*A19ygAwk%b@AF|r4^?oYXZb=3D?sAi > Z`#p > z9k;Z%GT07&lZlh7e6mO5*In~KXtq71C3Y2XqI}gaL1r<`FM7fZ*ef G > zO!{{Gr-ga0(rA@s7<<;xfebZKwkBdC4>(z*Oe*5_>~j+sASfwe*n0Su`6h**I?A}N > z|27eRl`8mX>~GG@VDCmI@Brx>;O#Q8Qrn1%(K>wA1@1AqK301ASk&>J_EQ > FcnaYc^ > zi0%;wJnTgI%FoFy;6!^?-xX0hHv?OuZlJ2UK7Q=3DbEHMe- > {}E&CO6a@=3D zsgl&ah!CKNnnQDRr?S~?(HEL_1NqHj5qC# > 52^ > zhJeM)3WtZlsT2tbf?C3BaeR544mF8@7#~#bbN8N1rM|17B{r9UmnOOWAji(j( > V7D7 > z1efxF?e(opWk3()!2(IyS?vAr?O)A=3DJBv1fNE!M7n%=3D%AA8b#E`9QjTX_n~5u-U{b > z+Dz;427cmGd%8Dq`dEb4bgm_!z ^wM9? > zpX- > W=3DJ#=3D{e0#W!aTUBa;=3Djs>W)4GWQZmxtpyOL6K7Zo11<3u}Wi2LE+fVLSFsK25^ > z@V$KLguZolbd2u(@h4w#l{Cqas;dKpff-KV?qk;hG*YGj;r=3D9yH6R@{hY}#|TtTSt > zmTO&HOBPdh?0o^J2`_tqXMW~=3DN=3D(?Ji1SRGOwG(!d|- > EFP&Y*s>3Yf2)ZCTs)~gJ+ > zB~UY;mi|W@ple_)k3w|ebb!R{rl3z`w!c0$h#ifPHYOJVk!GdE`-S;?x;Ac{qB5S{ > z@Qu$^>2$&0-&8%_;;orBLO!(AEIoU=3DLTAUKh=3D0|Gcncm>@iN%!3arr?G- > PPJ0?Kf3 > z$fz}Ni`sfN Qo< > zcFA@>sYzf$JH9*;Vn8p?^Dd7ytP*+0lJNIab!oU > A=3D > zrr`t1v6C X_KaBxie|nwb|o$l{eH#^hvv27 zV*^8C4nV- > )|89|D7;)EG3Ur0I18CTRMu~E>ksSI{5}X10>tF}i(iv=3D3MDp@TVUnD@ > zypH+2>Q@S;0CmQ(uY=3Dn-#T|_s{tX{uhb-jA0deid6JYg|I2n3)^nJ|=3D>sVt%SV(U( > z?iIt05 zkM%8Ir+NUqBM^|zWAqoB{hHKnzBmy4j4)qBGmppNr`SXVFQ5t$ > %Qo3*6 > zyc?FH@#5NSs;v(P!@qBn2)u){rXAq|x 9{p > z3Au^8*|E0kU3TQZffoJ<{1n|I&m*J?ixUslWnAd_ +Y > zlAvR!&IVr%pbZppSY>*V0iXwErA*R(HTzz{0+j-!{yS1SZ%lyv2FH?J5*+QJfCosl > z1J)w^1k}2x%F*{{Kl6azNJfb2&pvyZo0hu8MyU9vH*H&^Y_4AkxAiu>HE > 9& > z+g&W`PbrkV$@XHQ+}ptlo(~Rc!M6*Xy_fr- > bY)T4%ZNoPKw)W&U^_JSvDu#$&>8io > zO1O+Y(IvXa8X(e};5=3D?IT|h=3DGAiptwxA^1sb-OZgaoXUvS83(PBK8FLxH z0QrzdU0POlJSBhr4aRQau%5n?f+K*Nml7mUT$2;LvQG_A+4sMDrOZa$ZGPr@ > >iU79 > zi3@!1Ky*X|+M@`?YeY*+^t{V9G_!GU-0H0TBi>hBItQt)a$Iu=3DLJ- > >$^U|&6iNl<@ > zPYb;{C?VR&DurQMel+ok>DLH`c!=3De_N!1F$mIQa29j<`>__mbx8=3D#lxE0B- > nth*NP > z@sNR**swh_BLjY8^%fnDw>sQd!t3+H6XcT(U@yOpoPTX< 0o5+h_ z>yn^dW*&YFz+1f!*(;z@OTJvzWoA4=3DAfS75t{SM!dG>*5Y~#0Jhr4ORXfP$;z;U > U^ > zWA77JoG$v0)YnDSbxzRhX!i@7KCWG_1N@VcB9}SkLF8TZ;LQGL1IPQSwo@Sa > #=3D2pN > z3JgKVVE^7%$NWi(uI;fK;cI$#HwC9B>RfnjCzr9d_EXLiHj@GbW$B#e#HyiX@}T > KI > za@vnd?kxM{;9=3DJSjwjC>6lcSr=3D{}5&xdD|0Y+vRDP($t)Qo1dKZ{*|_Y}*Eo);0pp > z@!8BlB^AGO6$qR)3<`0s*lq=3D=3DqADl=3Dmbrh- > `^5g98TqP_j#KdfRYD|0YNGFhOt4#( > z?NrTi!rP!bJ%ZB@>E%$9^B%u|f&W51iGphn*QSh MXI4g > zw7gG)yQCNx9JHWyl{N36dq=3D1J4mj8}%};|vkaFsE?Ol&z4^P`Y3UC+)Xxs|K{BLcv > zkSM;AL&3JN!;M?h05(+d_Fo(>)hN_EO`>#}bSTho^qy{n 63 > z`1laM5&D>z1%L`3Y&I>40p%qzLLul5Q^}{yERk2Z8w?!RSuu>$pHb!G zcx^LXH-^>5KDHW$Z3bz(26Vr^cv&- > acG`8GfY;~Db0kxdMk7y?V;^X`+_6IMtL*zG > zyV_O*{_M;O@KCG{9jJyYa?nZJyFX > ihx > zP?4nOG~s%QzDZwVK$^rvGWJ3#a!bK%Io|5NZW9Km_00Sl23N4EsH#o@9qg~ > d{ri!Y > zC+;9pOnda(UYO#Y@tns3eI!7|l#%Iby7sJtoAY3HduOpXviLWunn_`reL(EigQaL{ > z=3DALM?KxJI;W8he%tA*wPkT!DyY$ruF;#wfj^Gzg>dD4|=3D+50s*F3Mt!nKfg+Q~_z) > ziT=3D;AFTR&olp5L@zu;OXE<1NvKP@)4M^|@`HblrofO_blkp%W}YUkyI=3D5L^gZix > |! > z)^Q9F9&bY9T~+h};#wQ5%U KA > z?QA^y7}=3Dq$I?DF$u|gzDGc686)u0=3Dk#|p(0;a- > 2i*w9Y1kWggcEAz > z*_}71r;fRY0@Z9y8m2k$Wum@43=3D^2cKKrHgz- > Yc~Qsu2kWQ?O0sIz*2Ug$6lF7j$V > zgm;^o^3FX@qVSHI#7qv6shU8Feb7fbZXAEid}sMVkq16IuRQ1;ysKUL9AD?7SN > bAk > ze_ @ > zi|8mSqO-MoaYaqA_#}w?c- > >nZVF_01+Vz#hk|Gxaw5A+%m8RsRw?ytFei_w|G65>` > zdQ*no?vCJ2DOCJhtdJLw&dfnJlbW9w>ivXgdw%!5b~1xcog- > (Rz@ruT7zBf!)UIW~ > z+}{InZ@Gtmb3yRH7$>BqUHbvP0^(Xl0CmQ1(9GIzcx8Ia%I$IVqzg{eoUO8`A4T` > E > zd3zjfS`K5JePOxJW(T^dd)`SAlgTxp3aqyGynT-|fWvQjkXT&apwI6eMf}u^-^WD% > zT;@j#c74fBK8Y9hm;0O7Qo^e_{qUuZ`d?=3D-^|Sjnt<66f)zl>i|M`KuK_0#(;dKUf > zTyu*%V(L_|%5}>KD68-34MzXKecOQ#quRyRa$}zf)sk)&P_s|_JkH80Dsvcr!PJy2 > zBhMF&Z+~w<)_&?`uVCFZDV!jguEcgWTS1B2X8a>zH#Qw0Pd&mQ_TiBwOmr > ^+)zfc4 > z^GpqT0=3DSDUtSU2xOaO&hZ+I5d`GHm<&~@#- > Uk>=3DDjJl057_mpgxax|_Z7^eE*fBXa > z5_DRyRueL^-#{qF_C_l)FloB z zq?H(mn;?yp(lC$^WrTEhC=3DwzP6G z&d#~-`-<1=3Dc^R&VuM&WIbtk>!Z2O-fB?hE{^eE?3&}h > z32!E$+=3DMZ91HfdyI-J-l; zd`8fM-3port=3Dp~3R?cXIgSVn_*SYG3-ETQb^?+RJ3slcP8(QdcG(Bjq`$P#4Lh~m@ > z7tPMl@iqfxzh@Lj$xc-Z{m@G- > GeWnQjy54wnd$P)o_C+gN2%ORIn)>Esp6M`Pnn4< > zB((K8n`$!Hg3i<5vVS>b?uym}0);{}e~Ya4L}2MA@OU- > |1x}%Ug?w2J9*_~UVX)q( > zY@k$KooG1vIZ{m$`ZTs~?8|yM8A|70H^1WS#chlb- > qV@_uT2r9>Pn}r5o@L|EY*YV > z0`B%7x?w2<&7x>az- > wv&LHgaWhjI>DXsm&5fQ9YO_OSWp@u zb5oIcpb+-&O)0Z7Qqdje6bbgsmJ#!)P~nO`J~{|6?{YX!snrY(k_RFC$-Th|V9j=3Dy > z49ou0s`$7YG%{PTxoXgtTHnHeze25J;ZfmzrKU6cD%1;X*foA{)^He9VLu=3Dee5W > mG > zeC0Q>{G%|qM sD@ZJ > z<;$3_`cXbtBjmRA_4R$A1eD8_Du>OjCU|#}8KM+KltfW8- > >7#rH>p$^Y|^sG289!| > zHdRms8wY{w?8%>#euLL@+*5Y8jQn>WLEWw0N~ywysV0f{5)U3iJIh- > cbbiR*r7a77 > zVS)P!i+KrSpl1)>!o2Q4^BmjLp~uobjI|@}OC?|zyP)wQ+B(JHBFD;E3iMTocXVG- > z- > 09mwmTH~lR)m%GB@+7cC;RyHm1FzRFjqI%bUO^SFxY(VYH4a3v&|xirlZN(eW > rXO > zkOskP_Dj%&r0MEHxoR6{P1*XhnSFXFXI||KOY7g9j5eoi*bk$8zBOE4zQp+N{lC > TI > zK2h)d?BKT~bo>b1DKMH0o+hO=3DyggY_XQ%=3DM7x(zk- > v)o z_+m_QYicYYDAC%SyExC8-vE_jPh<`{VsG!H*2~T8K2d6|asi2}- > 6X%T! z)|hyyfz+tFFkx6RqP_&YSG&(rp=3D?#=3DpgkbscgtTHuAsy6le(*8Vgm~aL2BkS2vrgO > zFQbL>J7nv_F|mtz_7SZ(YX$}e_)4Ra>1*8qo8kYtn@kh#- > IZ z wj*c)&TK0t? > zt#dHZub| z!m3X;G*v}Y?r$-#&~5>rG)MnW%jrZ8hL9i1Qqg^hz!^Ov@!- > wBgQsaQyi_$56MWm- > z)Xk07E Td^1>N*IfU42cc zCuYy}Ot$z6fplOYYneGAy56v*u1NR(Ur?<-b`G#QPT>@vYV?- > nog7X%sk_s z=3DAO)dv1*O&5?C24L0TI9YxyGJ;hVUo-`!T#veFI(LY3b+09J4hoP~d zbCM2u?}yBTP-|YUP+qtE+N- > $^DD4ERh834`U{Wl(32+F!&GG*w*(b^I+4z#dc83OR > zCn7>l4fL_x?9*;jy)|*G#qLh(qH~}RH7?H(upMNOHE1^|Hi+1@IDqm9JoS<_Zc| > ej > z3Z8&CV3V_CDkk}*`v0DKjMyd}Id{-f#RBwOY*f1eI#tq`!Fj{@m;NBCNlE8a>eU~Y > z8 mBp > z(Gk3F*A%@zOR1A-{J{xiomtp|OAG 6r9Of1 > z_qY)Ky1znDPA(&@AmQIc^*awC{m}V*sC;c0{E%mnaB > H* > zI-v5#cXLVORw{Zeno} z^X4p_NyF%6J#3+EA~(b3N6*b 5=3Der > zCqTxwwTBmc!ZyUkW438cqKo6~3A$L{9`WHdPWH>JhzPw!ob- > 6zlU+5lkdX0csNof` > z=3DHs3R{$%c!jWY-L(1iNki2w*wl^LVM1bse77APm~fd=3Dd*_?_t&Y;KcLqe(a$=3D)XP` > z>Ec6%UCl#6T@hddYYHGJIVAJG2=3DrLYPoj8c^4nJ_bRG2N-dC*`F|#5-S;UtJ>e0bt > zlnR+ZB=3Dthb?WfT~!j aA2Alf+o0*tL;??B*EPsz2Ws$pdWHO@;xOk1k3&u|vh > zp}c_Cu%2@0gnQ@1wfn`1yRGISGZFlmlSh(mImgW03{uR$NJ;J_u$fE+*=3DXbq7; > J|z > z-koe7xF)8|F!M13 z_3jO5;_U HFcg#aeR!O(+fVJQK7b9MtMMs2%g|PtM%BDgU?;zlGwD&NYbY!ry > zaz}XD&-G@E#(;cK;SBE}XW*7zV@*tT;fcyq-+GV(Errrw(OUXZQ}3d;`I)RHI-UiX > zDR+am_y);LZvn!no>FLCRUN2vCZs_|Hx7cMBdEv*^2- > z5u_zkX$71U3+l3kS*fWX9U)W+DCPZ2^c8}ROC@eFhrdvpzC91gvmmIL0*p7_E > O7gN > zS#Xevrax3@yB*_qS{5YGC%I^DEU^JJFg&LEH{$l i > zL%tUMKq%G1_i(+#(MoQpJuew5@ZD9{)*j!05CNdTrw?Q#dz?Q;<7Z=3DH!SZDyX > n{Of > z4XP9- > f|$A))K*H%*QMInSR$Bd1_uWvpn?EY{~RoPDAQPT zzB7F91J&kMo0yfLB51y13s|pEY4#cfrvYJOmSDCr@0#!FF>KiZD4A|5H1igeGj@ > YK > zxQV&i9p!i-x|PO<=3DkH%2E%_FEo4(SjjoR?77RyBFkB>Kx4KzR{We- > #w4(vmfn?Vru > z5M(YsY;?cn8G_u928FNF6oMLqB-D8yE%XOFPb$M2G{8zJRR#6- > 4f(+RJub`mui!Dw > zM_#(tjGZa&f32SGcn`#&lCD3*PNH9@f3L9D(Hu6~HIWwMWl- > }yeK=3DrbpE2k2XKXQx > zKK|>C+F6FO;GY<=3D2(F+mj2$6NoqGlZkUpT 3th7&!6 > zbP-Tin2M3T)85811YuU?3ILJenB}UpBd8^o0aQvo< z_}`vBN39#^Y3m@xOZ(VX?FwoxGCIl3Kf- > y^iZn3c@baZ{@GX$an^4lQ&IToF+yzyt > z*z3lX0?e^8|GEZuaKZ~DlimYRfAc+YC9 z?^V^TtuXHm&zbrSsPxx&q4S#7G?#bx&3ekLO;tr@Ud=3DqrJ`h`(*Ik{x;?HSQ+$a? > S > zH9-u3TX+SuMGox4XV1DlfVepmp{tle)r7qvF;Iz1Di- > #FqXjnSNMO_Y*e|TkzT6wL > z1$AvJl)O$4F#}*A`u83e8!H8U+=3D8b+0KrgXs%rKoOL^u1y`Yr&m_gvx1 zw4lb#4#?g=3Dg7f*l&x+0kn&8%(h>dUww=3DqnEuvh s9+9*KOCp;tUhBW(x32DqHX > z5kFqIUxF#;UcGbQ{H3~qRp0!Rk@o1=3D!}ue&nkkAKtc*w(`|JA6A`PGf4>gV=3Dgxo+ > ( > zHz~JAR2gCp*}SK(FF^}5jS6U9nZT2y- > Tw$NHRhbR4jJb6*q`NqddweCz1`?z=3D)7wX > z_~T}l`?bFRfv1yejU;70+jl(4)h3eq@k$TD&B0|RdK!3y7zS^FByLnSmVGhnTKq%K > zr)zf_Yf!|*&DeVN1&XC{#=3DBBYP_Y(t+GPH9Zl3Kg)>Bd%$3tDXLfc;;&t8Il5uuel > z35CC;*e9R~9Q9K9T4TK&=3D$L3oLOTR#`uzYcVhC#rB7`CplXeZeMV$KxlLOd@j#+ > q& > zT)!3SmFeOYpL*7jpzdPJgO6w~zghYdm$;R+W|$D+@j9!_cjHFBI_DHXPT_z8t) > MFP > z2e~I-K~cSXx8EG)lzE+7@73IePhx- > ;@UJ{nm{8KnY%SAHLOCV#1~fD8iyKp>%aL#F > zmsIpl%SnBU4?lB*m{#?7V{UXC)CTwMV{Gy@5)ifj3jHgWZjEuDG%Ft$h+)3R|5~ > |o > z!Y|X#hP3zmtfFkylmZ|bY#*Pj{t5b~@c`e_5O{OKu>+&?%+{O}sh{`KEt_~J>Bd9f > zjk+JP>l%0^)CQN!yZvp)f{eFpg~;}^RzSL0>&I%@+1CUgIfD}u`UoPFLs2y%HO(xf > zx|#^>GiHz`b90wZ!h9g8LMV_+>(uWlbA=3DdmrEmumLe)Af > A<3 > zAy53~u#IcTRdgEwsrv6Mxp*edfR&qN!Pk)m{~Fx;pwv<7RGjvBK@Mt FM4a > zV98RX`YFku@hJWumT!PR+zIT4kb>D%%+BQ7ZzEb|anesQ(b2}hx*$X+K2 > YZ7` > zt<|Py@a@dFGoH{JRRzlE{ZN1Y0;o67^`sdxy#!K;AA3CjRO?HMon3>FYmZmRd > W z88&09x<3LiRKp7euZ2wirE51>mytBm1a8WOcu0C9)UXu~U5Q- > dnOip*0`sN2Ta~yG > zTk<8?uMuE?%w`OwAB{cVw_R>P37RCGQF-%Oz^B9r;`B&#;(g~co|yXk- > (q@MTF-Nx > zo1vvwLtP!yTk;!1(!RPQeHk3_H<@F8`yl<6TJH*RQ+AEA2msuY3>3)&;e>FZnd4{` > zO7boYRp6 OZX > zj4#Px-;5Xhrn1%eHrN

ww@n=3Du%fOtBB3{QSpto(r}doW- > kGO+`xCf>(h{Mcc%|2 > zt$Un$fWncalexpQqitoi zDUIY)(n7BRmq^X}pJYIh%m+o+W3`+WI+@r+!TXV34U&h}0$zn1Mvar`mzpDV > -5^$B > zPbdR=3Db%2-pupOK81urupl9sj(A%g8wR$wx*gtJ4jquf!&S+gD=3D?y9Sk$BORpzt0L? > z;X+pf{84kj8YP;3RRBmmQW`-l>E+(AjXN0-2- > pNELfG(uNbq{doFM5MP7xlb)Rgq< > zDqwePWfp#E+y>RB{I_p`gy6G`KX>@6_9vS_Em}Mtyc60P1vI*;yFW!pM(nb2N > DpUF > zefOzq!39EXNeJ@@L=3DF%TN{&8GO22y}5i7(U^sDQQGn@7~zvgQvZNxZGJo6q > %wi~ga > z(y=3DM|fUJtH+A6QC?*Fza1!GP!g~tV6bNagj)DB)P;dHxwQk~KK-r%dW-3aN5iiL|` > zNGvfJ7Wo9&gKi*IIyT}@DR*goUypIZZ<+I6ACF5+to#c#D|~ztPNuJ_f%DRjE|x$z > zdsmsa%oI?gEjOlp(5KMzv$Hn4pha-N^@c$;w+- > lbp!I4w9ZnT_6aMT~=3DLLkg!$Rl# > zmJz2Er@uc2NnY>si{>y lmKVwD3GSE9z( > z?&71G3=3DY zC#plA`Eijr;Y^1rsMi++s>yZ#WU^`NQ5m!XMnI$|DS7w3iHM}8po?F(;&vMsjtwb > k > z>`>U+dX9!dRL4QMsVU)lYGzM`aOiO{7PiBJvqnDhGUQ*}YwJC~IuGz~%;=3DAmoa > 9>Y > zplhI#NN1F|kBi%SY`pqTkt2&*gan$Rtrp_SfNVS5`SP&%cKz!WpBUS};GCR;%E(# > R > z^_YjN5Z^6e4F;o=3DQC6NloxnI|>OCXd;)S(!rx8dqfrHKp8PeaM>_sbRO|D(Ka`R@ > K > zW@I?Bh&^2{duvaIqzODnLrh~Y^{voFGEA4>A=3DyyxP!*`|?A;F+s=3DCW{;eE61zN9 > N? > z9e_LVMk1B;D*E^0Ed96vYh)hut5+pl$dg%d>){_7?K2+3&42tdxiLHWa`rh9Cxiw > K > zdildOB}k13;#BI@N5b9u5s-{=3D$;2+xtLvKQIGYm??8G)+0c8Xr*{JN_Gg0Qfv8KGp > z29qSa9Qa*1_;62C=3D7pCyPN$VEQ!2ynSBj}H+_- > G^`3j|E#&2Ze@Uf$#4HX(7!w64j > znP3ef7_a7y40F^gld>w|rbk(G4`D%ez@J<&cYGp3uh_?c$Z}veo@gHAijdR7d>| > sB > zPEJGY%z79f4~X+!?c{C;#r > zI;QxWdy(wIcwMz2J)4g2b6?=3Dg@SvGJb(yZxAmci>XYd@Y0c{E4&`x^POE{KL- > $Tm> > zVH#O|+itm~LrFoE#$3*NE*7;zOL2sf|3EOE)h1 3i$ > z`a{h>ZF8m>(7smICD=3D?hF}k@Tv4@34v%WvPGO#B%hPW8mE}xi_|Na7k;`P2k > G5`&V > zbTCwbgX`hWk6KUt+7e|3#)u$*z3Gd&e6v{rZ9NK<3FEc%nI!Soo!)Ng-- > HdhvEgEU > z{Cs?_fbjgm1avNfaP3u~kSODWs2J|uuqg&!kLlCeRlbpZ#Iv}Yv*qm6c267f`Jn?4 > zVwPI@B*m}KbjVZHL5hC;R$- > Q%t!bC$cv! > z)wvOkEziqKiJ&`{RWPK1Eh}eL5H8q;U_W)7aFGlg`uBse5wBoKeoXSkk r- > z%&)-4_CY5jfFx)|e 8zA=3DmOsJWWA+#z!u@xV`Kd|iJ|JXZ9x`@ > z2EzH*bf(*)&fXR!eFG_Y9i9B%XIB7yRdBW@nFk%sfz99+Xx0)(%!GP=3D7ELhGNB > Hp} > zg^U)eKgXYOGp#tybn58q*RK!LBwYW~{EselBvk=3DuUU&J#h#3zP%~r- > 2YOPxyr*b}> > z3q0gd5rbgfktM*1WO0f}dL{W9CR?V{%%L#u+iY6DF@FQQfU^+y{A > DU;X > zR#66CvJO+QQ$cDnIm(oXoc6?P_uA_^|KUL?)II@?Tpft{H}Ye=3Dw*#ZT|3LTa4v@ > bS > zXQ2k-8Hx)pi#iY88~?Tg=3D2DXLmANI^t^N&Vs#GuBFpzaroBNE|- > W+1H2CZtdmOT(s > zZgSeNY}nRALcorDLGo)3=3DQ{^a`?RW;+wdJLIIKxGg7X#P&P}*@R$6Iq0@^M8#Jz > wr > ztn1xPW8#lmI4)e5<&H{glYvjaNobUWZTAXm3pVDVPGSFL3rkAIV#%E?+l2M$? > Zisq > z#XHy5OmJDK!zaEyTPjnZRI-ATP)KJ-iy}L{#b;~6y>upU0{S#bGsn-7nGsJXyLNjo > zLMY+!R>Z(9b1I@BP9A8$*lyIXDei)#T6Np)4zK~eo3RrHLZF8gm{?x|38CV}aNlp< > z=3D*2zyA;4%aEYmavnOk+s`5w*R;;8ce`I15vW!_)qOoHaJzJoe+*lv?tJId7OK7{V~ > zpqOIVa8Y}i0qPTM*RBa&UPi9$H6;zM7 z^{U*ymKv?HW9YA=3DB*DsKW>foBG5?DW93fh`XK}4diew0|H4|=3D@R!XDw6lW > N<+%Xc6 > z*uX5b0+ieK?S0pJGUZ0)_9jDtBFPKHA~%{7Tyb45m0><9tlfY9r}e+5AW_k~A9vG > h > zR{hELkfDs*>EDkDkK&3Iwm-ghxRdaZvqnp9`X$ep- > 0s*6L=3D4RuZ>k3UeD`pkpd=3DK6 > zt|+>vIMR_m?sjh8n}VgWM|Z2%&hhe>OT22N`C9bFVYn<~H~%HwuPavk;h_8 > a1HxFs > zC?pr&o7B*EYiO|&qC?+ij3dJ<8sj3)B2*XRnu4Mt=3DmkB#n yJ6 > z_T2M^oYkT|i>>8fDjDM1&tE6j*>hB|2KgnUJ((X9*XKRu8xjC6zY & > z-=3D>}9(>;$TKuZm5if=3DKT1okgw>fCo)?GagTlnO1R=3DF`3F3yNUOH@5orE$~k$(dLkf > z(TCYGCp9AK9dap#cOY{0_7f8Xu(f?2_h_M+u(8su<| Gf > z2s#zs8w8oACvZErtPjc?i05oJf~VW(I}_{;K{C- > m=3DTI!deWV<@u@B+g9zOd!H zUZ2rG^O9_g;LQnV5ScX*xy&jLvX?@rQb;I+H- > !7w?@Nm!4fF|H&)>RG9@xH#VA%Kj > zR!#_exji6up2L0WFt>PC2xii(&!l(~ @ > zc-IFib>qNlwOUiVS#85^UE4-1;P8ixKP)l8w=3DH4t-fTR8ySt1J61D!MiEH|v0C_=3Dx > zFz6F- > O~8k5r=3DB6+gEI=3D%B7x=3DU*u4~`0Q7Pzh320ccRxp#s@Kx0Fp!iR4>!=3Dm#rLm2 > z`KG6~&Y4#$- > >{TibCEs*RuYpN$0HfTgB&xUILCA~201p?3&^n9&SN#@fN24eOKIXO > z`HDY`2rUn&$u@lHwMG UGvT > z3oUo}&W% %C > z#_9)im5A^xI<45i1Q0l;cSeKsaw2eRK ~q > zCbUK%D$20BGqhaC*47FA#LP-xV#Y5n2+6#wi@&R8o- > IzYg)#61qGsUJ@X>V > z%Z`Xr4cmzOVtfkYirC*aIQ5$;O}zWyIx9G&(KO^@lgaB(PA%01QThY- > L2ha3l(|OP > zN3)h$K0wx7GuYqklQI@IVNK=3DL`Rp8Q^vX^-vNGz+4g_?6=3D- > neefemATL|%SeTj7KM > zi(z?;D<*`VPv8M{hL3f?hBMA$pWfugEju@N`KF`iJFMpOY`2ateJY#xi}@UiJNwce > zN`jJ8ZE*e1jZka^BWnn8D@>l*Af%sd#^cl2pj4vJy1mIl=3DN%Oa>!#&VHzPYs5$uL > 7 > z!mV(@!YY)C1EI1tx4- > 6jp{UDZBXZpNJIH99hEwb_7a=3DnWr0)3mf5T1uR&$m76x$AP > zwpSBL- > W6Lwhwu?1YL_c10oCGV`&6p0Vms8N;2YKq5lqOAT=3D$FUzlFDc=3DmVduyMC6_ > zOF$m 2MRC?2nH@L3 z(0IW3+)M3IHUkF8hDg(5QL>*skU~v7UeRp~- > ^iTXueIx8byvHT>hu$|i_OeooSftn > z{-aKe$k>EeF;)|Zy=3D(6Lm8Urn7(VItisl2Fj=3DM9R`?y|s)lT|raELd%d6_~m?1IV? > zle{9X1hz5Lfw7lDzTFlTZ})&K#0N^0t2e1ce>~O5)k-R;1MiINT!C7PF0A2-K#Z-1 > zwwT3%%- > W88*^zKTEx>j2)~V9UTQ`* > zG1u8&xQI)LIXEU+7qGK_E?eT`pS#V-- > kjb0g10PTQA2cnH1VYt8=3DsJ5D~h+0OaL57 > ze?~SykK7UD-{WpSh~4E{RhoEJFSJ z^mH!8UCg8wEgEfC Zo2 > za$ipL@2iXRNSZ- > $C;|qoT=3DNbVUj|EnU%Bdsok*%vHNYbA8@R*bE^?L#N8J9Ie~kMH > zFw9B6K#Bd`Ym31tFdAsHI3MEdyN7=3DSA=3DG5nDjuow=3DWD^M<&la4Xt=3D;IsR$?! > %!FY6 > zxAF@Lz79vbF8@7lW-!|iy%`)oRCg-! s2WN^ui3C{($wPhZa^$pzWR^Hl3F > zc&ZBV%{(tm$ITGq8=3D@?JF8>KKAp1gwkb_o;?#<7*ys^Xq<@i!nYdjT0T|z%Pgw > 4 ztoIV)^@o0gVvol4b7`H?AK+CcM`O)WH$B^#*Gt- > M7|}+UXRU7h?~TB=3D)5(t0^!(%P > z- > F=3DoR0cB96%iSgM{^=3DdV&3BJ66Ylk^Qk3Uc)h3 > ea > z6- > G*5paN4otm|)75~9_6q*Q3NIMMfE3AM;i0o~2>Hxh c > z%y^8n89vQ=3D&D-eDn;KkDiQjl{qN(-i9_yFw*OB- > A > zBiBEN7whASWF7GIH|kpS{7R@`fG;kNlZ1|SS*peVv > U`(H > zaVYw!qen0Z@!cKcSGpB73A=3D8RvN0!Cur4ax*~&|ezF6b%^)M#?hFI3eZ_UhWE > 37a# > zBFkv-I(?KlV%`3;97o-gou2?WYbb69sCMC}^w$$jzUo!Y&! > zs?&}rL=3D%;BSkRvYMCav_{~j?vquV$*Jiz$AzRDnGI_&Fid(8Q z7azEtztN{Iv(5ELHe12}O*6^l>mGf+rJ4EqtF4Uwg3cW}IO`W;W}25q)jeCqV18z# > zq-Bw=3DkIs$twh$*a%{xZNiYFJUuI7q7&x(kkL5P!8aqanHrPfxtAF%}qBYJm0$sYVL > zJP}y5v#0lQ&o)1+{dt-t@Zz-J=3D`&jA2_>QNR)`A@(X5LpLB}VHq~<;q7SZwF!x$;| > z#R0Eytal1kOnUP@MjvHMs~HLyDPCaRhCB2Zerq`U6XmD325HpHsW>9XI{iO( > 9zDJE > z{~l4!Uz2cN!HjlkCj+UAXff?Xr3X(h=3DdoBAEK(8PJ7R(~h}J#Vw81R+Qod4npec+p > zKS?@9gC>?~`QG~vYx33_7idtN+RZbziy$Ijs9cmJuO+ahuQ)I > z7j>4GjsZ~bM5RU9=3DeGqhs`Qaj5{uZ_Ud6- > fqr2rZ=3DVYP@rHCOq9pf8B0xw@Y0Q|UG > zM!?Zk zK5}5_1g)5_t=3Dh{UxNNJO=3Di}()%IpHFp4*FsI$6|J&iNV(owzH=3DB&%B59xumi^o#- > G > zl4CZhmtE_!o>B9S|7m$0I^cymdvG7&?O_?`#7_N(S- > csF2$3|xVaVFSs$oR?)X!we > zeMt|VhMvKpX6;A)H#I){1i6s9%DAJ26co_X3)-H!*m0eZZ!QVFvyXV^=3Dj}!Z?!plq > zDY91FFY*NplC{bSpJZin2&Vas`SsX`X8>&k{NzyAY3jc1P4G^=3DQ0zC^2=3D3 3 > zJ?a{X2<1ZzRJQRk(QPA<;g%2=3DeA5s%=3Dp7dnlO;Z?5VO|OM430 > oJ > zI(_U{Z0qELvqlo1zMy(- > joNCk?D!9EA9veQl0tLN@$+697R7ISA{Oj9DQM!muOqI+ > z1=3DBgs)uV?6T4LOq;iW4s#1Tay8pL^4qa6HT7!@@Rr>i&E1MsQ=3DSsF1QnUH$+ > VXJD) > z- > q8)8gNQeo`#0Z7Jza*>ERi5<^v6N(%;z%&RMDX=3Du`GthWVgqebN*;CA@l5yVYY > 0B > zM#+iz0TlZETa!j2;R4p_#Z#QaXE)T=3D$0BRX2r5Q4t#09>^6uv3f&M4abAd3EhC!P > | > z4%5A{^Hxm%&5>&TpO2L9NHgR5dlZr@E)-x&mF|r- > *H*VR4`LaS< zBGo{Y@aPv4Ea9}4gEHHGNdLE_B?jDJz1`pT=3DH(py7Ng`bivvR+Wqhpw8tYpsM* > ANN > zUz%6;oPCB&&k^}WT*2fbT- > rDFvy`mC$)TI2Nk1)Ikk#s3pnRcLwjM%Tw8%J{?)(t{ > zq396Jp$smKz^GYiNqjNEWssfjivJNdhnO1H7@}a55~Iz3qOL=3Dt!QDgUBUhGkI6z > 4P > z{MfuNHeOI7d~NQbki>Wa*r?}Ta- > }c1`Fp+#WtVnfNe=3D$kV|Z$iSMH~w=3DI;erMwnR7 > zw|sDfC*TaJFq5#HAL{Kb8A3bLIN<&- > v;f&Nu^nHE+8UqQ6i8*Q|L?I#SV*4Ae0`%z > zt4a)1#BM<3QKNZf82X|^ K&kxx^&?So zLg3%pOQjI?Y`G!u^u~Y!0L=3DCk1mikTVrbhz5Ih-2{ za5S}B6+!!&Ng(mtH8YRUXJvCYVRDX`2TJ*uWP%A}VGnN?PQFyp(oJ`BwDxOPd > ~+Gh > z11ab?54FD8tT;pt#a&0b(2(v5{`c``k_#uVAtX<=3Dpj=3DV}jL?jiM`j#Ib#UAp!sxj- > z=3Dhwr=3DWu+*@dTi<*?5p?Rvns=3D~`QgUEF?- > BXUNalABrN}l?3DDjuoNkTNq%YT73F~h > zEU9qnc}*FZ+ S%|a7B=3DtS!rdlDQnw_~vRNG+ > z0F&fV(JKNIWL&t`bheKJF36Bm&a%)nElwP|UYAbbp{}dbEmv%#l~W;aV=3D3AWI > TI9~ > z80}SbU~>FO|9I4K$=3D- > QbieMMly`ua`72+%KRdJb=3D5lfysMX#bhAlGBNU zYV;a0O%l14Y*9 TSOTON|2ebg0xls`JM}%@O`gK > zmH+=3Dv*cCg)>)6&*G74T9ntba~IB- > 18Gfz;WMB^neWL%=3DF6kixs3>vAmMHr<#a{F%5 > zSV4%6rVjJ{9oXWh7>e;x_5=3D7- > ig|jpSD}@WGmAamQ&27hL?Yh5&;K{Az^2>@Cxn#6 > zzfJ#Gsp175xffi<>k)w<7Fn=3D7Zz86*>n?UC|H;{y8wZ#L#ygKG-ke|AX!c_CIepD4 > z!jPQ@A+Wb%GhI- > D?bG#nj@J*3uxNfyiMeHj>21xLUv>Py=3DMkD{x#(ays&Q*avQoVl > zpg?U5sxJ^- > s_X5pS;xuct}1cBNZRsM;C1Q>jlfkS4cPm+6cweCmQ2X>*vM$GDUu2u > zrfP1oY$eRO!g>r+1gwnimAE|l$MTGy>m?joS%%YRa>5Df5UX{zACOD1{BvRzH > Y16t > zLx0%NJ(|W#Mwl0yY1o;LJPJnEKBM09erE1AZv4dMtuK?w;E4l- > Fr_U^mpQywF7_%T > z8z9&|c<+j$K?1t6WOy$cdaokTE_CD`#p)wGob|89EI52O93E)Q{K$ZJ9lbN2{ami% > z3WLpyNKP8CAgc&@A(`}Bq1ivs$JfWbO0b+fTE- > q1jNZ1iQgnD$U>`mLw>B3j#@z5( > ze<0^A{$CJLLB91m|xEJAm%G@|mlPu=3DNp$qd)CI{Byi- > XyKLbhKa > zbm_vregABHB(ocN=3D=3DNhk`LW3P>i`=3D<{zk1DIb7;m&+Ab29lq=3D{ zxc~1)=3D${q^{v@_+;IV8Ms3^28&cY;vbd1T^P=3D=3DGQD6a)fYP+^ngHK~6L2eTcBMti > - > zmk}MRW6!9~7*x60swGmQwgxUcS`NlU#^l5ISq7fW7{N&QrNUGGL!nbun!*K9 > fOo`p > zoDYA)K^cV*4vEOWFJ^2LrNw%4F#I+xUC(W_5t!oOcgGs- > ui){uM_Y;pwOERO_?nhh > zCN+^j*eU|(H}@1|DulXQO5%eTuO^dyTfO4288)&1e|1FEFrt)@TOSwP@avgp > !5R#S > z;(Or2(uI2$O)nW3{}@x%qW#F3lm%p!PN^Q4k*MIfPn}J UO22MYfNY*&sB > zDxGA?Ob!k=3DV{1Vo_58kh)#krYA;F+Xg;(uT)f#UCe@8A#FqlpPd{JC9j0)vznO5O1 > zbCL1e;H2#lmE&>{?pCIFbEX32jw!tI&I(V|qNPc=3D^A~B*obFWHxk1Fv=3D_jDrmsN > 1K > zfJ`e~-pb3FaAgQ-;P3F{-`tx{h7xfu=3DMjtAzGmk8RNyMgl*Q-D2p;#e8CCpYYhflt > zI>`}(23b2x=3D{^bAkgx&<@Jcge$e9L23T@|C6&|K3l@{*|5%HK1McZyCn8_TGr| > TU+ > zF~5LUg%1V%8%0BV*|~kh%Dut%HC71EzyXNZ4;Qd^PF~!HyUjWo^B$Dv1@}q > gr0T7w > z CF0d9B`ABCaQ2E}$11V!4Of61KbmH>NOgVE2#7kIj!) > zk;#F$gI_*X5(qx&LYuNo5B%I80hsrroF9vlFiH0ninvj3>2O=3D#52$M68oXT*Rb5*D > zJ%lB&vO `^2 > z{8-1a!*faH>RaFfTUNUi2qCgc=3Drd7!Bi{M)r@?}!GWRRaP#&>6-0h5~^qID()!>0O > zXjOi@8lZPgi3_oBOHlc95ALJPVsFWOY2TUf=3D8qUG(3X=3DZ_JOcWht~V{6FSp*+w) > 9* > z7#~-y&DBS!pgoz- > xEr*zAK9 z*f=3D%7UE{!{l|H1Fjj#kVC@=3DoC8Nb1|M?~RgTHh+7{|~FK_mV%_#jkp>8RzR{!F& > Xs > z2OH*N8^aA&PGW+RcU0BM1qy^m(XM`@+)}j%P;i&R47VY7hx > qkyHve > z9D%(s3F|y|mc$MlzO~ E(bW+ > zaxDLeSjw0apb8aLoQ0v;3(wYcG;h3k40nrP{JDn1=3DF_b_&q z7z+Y;fSWe;Sc)>2@__0&Q&^1I5X990P$dj^^YI~)rIw^9T?&zVTi-^Z?jgng+Js*a > z|9b&ZY?It?5l(|UIdrnLr@@_B|8_zTYtW9ZRJXZ#^UlwdTqHB*mNCXnWUyI0K > 127i > zWZ4_!;*wMCcdxhkL@ebgXmgC$cIeXZ?$w5;J|@C3R$#STG3T#uS!A=3D$$dEeW > R1yd=3D > zOy+GS3N+k- > ;^Rm&4u1x zjeHe|F6#e{I0+B7voGoVb!|`!HkwzQDD4AI+nw^ppfKB9>_d@A4DT(%J8*jZM > wuHG > z7F%{w(cCew1__ovrMAOaI_u`M=3DVqS7U?VT!E|h;*MuJfgs~Yk_MS#v9N_7#TN > Tye@ > zhTYD$rd^bWWLQ2BW 8Mp5YQRWhyS@`gIr&htF@U=3DBD#r6X7 > zPHn%Ux2^UqN@h~+!u^)JVumKk28YdHc*w9BF7T#aTi#3uP7R=3D7K`8!$;M5Bpi > =3DM9v > zloJv6mNZ9}N8k~3fZQ&GJZBBqLx~RZUlPnB?v8!IRhnA*eQjnq=3DOckv4E=3D39YP9f > 4 > zx0}1EUE~rVM(wnlh&Tst&Gc$&f- > C*U}T > z7Si!|5a3@|0&MPZ@!esgnrqWV?&zpYvYJA%%UvSj?AjSv0?Ue<2te?aR$~N>2 > T=3D@i > zk;WK=3D@?1|3T)rZRs9V&eL^;vna &ip > z$~1WN)69}m29|3!yZ2zu+k8F*1AJ_Llq~_P8b6BEa(GlhK;k1+a&+Rc5du{jjbED > M > zxe=3Dww Ya5e > zoqbY{wWZvbeW`6=3DE>_jA6)!I!+hCu>=3D;mMBB-hjIr6^ba%1jIlO{0DzrRDO70A?N> > zHB5kj>%Jk9$rkL$#ANd1DT;=3DB$(N>NLZAy>S*!dV2wVQ?XWa5^hv6q+XHK?n-- > MxQ > zG1h%OC9O9yC%If5t*4BIdz)$gh>- > Z zpnE74osaU=3D9m@It_0)?O@qs5V9r?>b63mk#v%VR$Z0vstV5%b4Ff~@nLFDVS > bVYbX > zpi3#KXDI_- > zWqQ~fjyaT;zy3NjJSsllcUt6I^Vbja$ftTtRA0}&Scx4+{UHcXb^7n4kqqWF(zfBS > zG8P34#!ML$P@MDi=3DTApfjl@2bofNRe+fyVMyS(d- > XA~5}VSN6cplAA%P=3D_p4Yqn~a > zwEe*&`EnJ%Rb7FVX9mM@?&{b)0leLYBAdTt;`hN0{a)_%aBBIgpZQ*|O63;Q% > 9};T > zn`igV;AX&|r@oRSNiq82K?Q+CMT$ X`^h > zkW#1DVX$qsEf8L%&p5&ekmJXuPWVM3ovFo?&org2Li<;RE$_4#`n<<$hI0tj_} > Ln=3D > zRe8>7)Vn>Hp+sxMmJ4@o6F`sujm%Rw- > J{yCA}t$O?2Gbro_S0%@6bE|NUO73JAX4o > zzC%6Llj`Lr3V|Cr(`1i+0+ZzV2~LvYFi*F&#G_|0wAMn_&Aw)@GnxGrV|g_yM- > 0<2 > zy1O7o D9y$ > zp>qxUd2lC97TU5jN8}IB`Ag3sxl`T{Of4-pLa1nPSITX#OBNv|rTcQ^sn%oqV(uYJ > zkz%kv_Lgdw_WyuuP~=3D!6#a;*YD~9T&b`vi_fl!XAZ^kW- > 8FRu~9(wk+ock12GT_Ja > zM^?ezBrqptKLHwLt1X- > 1wZ`Mj#=3DhMa`?~<7O+}ta8yDtnyjj%r2f=3DTWOz{2DoX0#5 > zBUu(L;a9s>;Z>oZhQZ91Pxr_<9A4ler=3DNF4Qp0^199?RAz20GCe?Bn+b9QPxa82 > +` > zJ5v`+v>2-^Q{~GwYpt21CHi6NJ5O4gY7g4=3DzH~z|ySjF-GVJ3Or!j$S_H;Jw > z%v62aUg?du*uKmjcxF|2AAS@ymb_uqT2)dK(`QRRRk5H-x(Fl1Nd=3DL|!- > _c(_oRc| > zD#k3PDk7ITbtXoZ5)iK$vZUO53H_nr^4AlP22LKwD}- > ($0ZmQkxzN+R9ka~NXuma=3D > zy(lnzTW%5rD4mM3!MC~wzcoudHjVZh6qZDxTpdAkK@y;BzJZ~#T^v9k4YghW > Kn#~- > zFw~d`jVUez@;FFJDdRog+aP%DTEup~nBfe?o8EOvlbWmfQBsUN3;`Hxv%zNO > XKo0` > zymnf#ub?u{JO^5d+F%A(!)DkfcJqa|z*EjQpm8tr3CNSDL8ra?Dj417x?yLcYC?f& > zp8J`PD$&7_mHiYFm{7=3DZ zEu4XqR^?OoDa4yjI)r#-^cA0Vqt+g7MvT@+(UG BrsYiU;WdpU@ > zmR>$-PfosIWz(r5(5 zuqrLH0@@HpjE$MUkBwTlf$r97bmgf@T4rXivK$k;7B0{N- > z3e*X4)s#1kO^g=3Da{~c > zv1YS_l9RD`FsL4~xenL+c@N{2D0cxFvIf$i1A>M-q_Fh@-fIs)2l9cYB3gpk+fTrl > z)W<<5N7esulR#)QKLOZ_yMPT)2lP- > )(2&}?&!a(E{XC(M^JVebM5P=3DyNoukF84}qZ > zR0K_d26Y_RR97K9)9?pp!(G6tsReL|T8O#>x^K0>5n2Z<;CuG3H+#XDTnT7O*1v > I2 > zVA^V1Aiy2`0Xo=3D6&}29;F}@r?DN@lI8dL06fg+L&Pw}&k_4`+p72Ft%js1<1OUP| > K > zcK~H%3<&RkL)OcGIUYmx&hak8&H5Avs`L^|2OdX2oOsD;cFq@ > +3pvf > zS2X|~^&OIEqz$gw(@W;PH!5VeRs=3DUyr?F 7 > zU4=3DwyP@_L^!ey*x- > F&zLbdCN&P=3DUZTAj=3DRM#d`q0w{eI|=3DLTAq!vsquL%{u5lWtAe > z?H(2t4~%{`)yK^m|Ek)>UcVCb?fw7;=3D@US7@&x96PvAv=3D3>oYIQfRI5WKB*$w > 2^;3 > z&~ayF#0`*e+`x#+iQS@uKXrXjkNlr&-EU)dialphdi2!2fv#;W#&=3D*3tizw4<|e7( > zjLuPCB72T`cKDfh@9+Fx2A#lJz-ifkpGInFYs-j|k|gNJZ70eA<}HU#BT%R$jr844 > z_^<=3DdtZ^ExTevN)kTL$ZSsY9~d@Qb$cH`p$_dixGkm(H71 oM > zt)DTja#<97Q1XTOHjYl~g9lI7S0u_Ll5>T#X|^c|cg|oU9SF@Pm=3DD1l_yfEHJ^zH+ > zXNc{<^H9%bh){%BxQ2B;u4=3DX^M<*w)_@(IlGqUt=3D7H;!BK*3C`k27VS{29{96j>a > q > zd*Z$e%^e0qFF%5@-Fx<*r>vVp`y>(D0RRA}UAFNT%p- > ?4X5LIO8OnL6lhYB0`;L2G > zM9$fNK`EpoyB_urv=3DA=3D&^i`+e(@$0#E`azr5%vQ#B>y-8FXGAp8F9cu- > hUH$waRbE > z<^`B0A)ZO=3D*&-cUKnBt}+!IeuaVG=3DZ- > 3ROfnZ_PG{$NnMAK%X%Z_g<^eQe}K6mTGS > z<<~YHbP+2cO*=3DHN42 =3D > z8WK$DbOdZIOlfDTp4J%w3&eI9Olx$zA{FjH?g+aah^b7|7cMu5v5i0q{qP*r;ID; > @ > zY0#{b2eTG4>MZ2t!uklMlSt+nUBQ-rnEb-Ku^g*?Jj%`fBlw2V!m#&|0;qk(ix > z7nh)TAx3t0%o}K+1?TBaPAS+(e+RD+1G7F- > ?Au{0G5tdd)TPX1hakJ3TvR90rM<^Y > z+#BZZ%KR3C__(aao&#^=3DSckY7{A8~MeG|zb9z$~vO(vS+@!8o1yAX^?^J&qa@ > ~k)f > zOIv1m&s1ejnu|q;`|{G#eh(poDQMO&YIEAmeO+(4^M>v|7m`T|NM>t;vpZ*m > L4-Kc > zqsaY=3DgDd%ER@*_kCK7QoNyz-qE2~+(^>wi61TZ-Qmdc2u!gX|x!-_hOdW0;F- > OTOp > zHDC;2!@S)At1IAsPD&Dk3IiGmkb2AabM^FW6hLKog1B}?Qw|JY<+anh+b7{!=3D > X+}$ > zFuvZ~++1ipISzO)2)RT}D;- > aF#h&&LVPerTeMCI$`Fid9>&<+@g+1;reZCWo1qv7=3D > z3(_wxUF}>;K8}l#fx&d9J*MH=3DX@kDAED3@Ja4`KZEZ;3%O~IsQaX_T{i^X(^Eq7 > GW > zVGdnWiBZMj0QC(V0d$w;%tX}=3DM(i=3DSFoiY=3D=3D^gQ$f>x@7%TZ1+L7xy2$8t%nU > ePA~ > z_hxdvS8-a6) z_f9&Bj#Bl`UV~tRY)q1{x?CzQIcY(|vTo_hvB9C7V+!elvbkj-QgjuQpS8QXz{p&e > zOV5>148VMNFi4W@_CEZ# C#Ha|I > z=3DC2Ea@CE*LYY3?FwL2- > o7w4kPn93LgoLgo;Ga z&U&MLi3d9_ut4tlY7*dTjv;@%OQbX3dNxhPB=3Dhp0T{&VwWsaW2bXbrrrk?A > W=3DXSe- > zu8ysIXto_N*2b4 zM- > {9q@{JmR%9_ yTg > z9G)tE5Q5Y{ZXsama_6};iU6%|v*qgF@jt(KgTP?9R%0g0tRL- > 62S1{LY+8Uo@`UXC > z+kN1Bicu7QhJmIwI1Hl$rm)P!O7EbH{W32z_#q3T{lFs_- > =3Dgt@_z#8g{&FBl60M8q > z=3DjZ!QxW^d73Pc;jWas>Xa37s6W0xXy%cAtwDBkO;K!XpFn$e$5ZtLRx;<}Z``Ydl< > z7lF1;us<(h `F > zQ`K?a4vlk8w9nbAxN%8HnHzO0BJKg7G3^gZ49)!b<}XUpH6YtiUVE%Yh>De{v| > da* > zjd+5kK{DBy(^5L(%$4w%u0nYr+b9F4w_9gC- > !c6wZ64KV4{+qqvrkXa4qYz3afv?e > zv!MNA8W@rI(1nM^S?AX792oexy`xn<+;^98<>Ke>(Ozo~PO9&t#392Mo2XrM > P?$BB > zOd-JkVw- > `TR zGR>AiGBiOs(VFWhbAjIaeU`kO3(dR8OeM9=3DjcZ!EbgW_mzlzWAzIcef^>|5n)7z) > P > z?`N}!JgBPO#-G`?Nlh)kA;XEn5I%o!7zhyxG(l#d{FgjD$eMp1@1(i|qi}ZBP>;r# > z2Q#2DZZ1%w`Dmlr6R)G9K+M6$B=3DgNC9LR6$Km?HB?T|8ei#C$|l3Ue&xr(Uvlb > N}< > zqLB8`- > ^A{1e!kwo*YW167B3HSxyyY3WS?<5jjRBX45I!39@CeC0iABDv)@gHrXu{1 > zWi;}ax7vNDBlw$X1}3T&uI&fo{q4`ARrj|R8IvS0q(D=3D*V;>LtXgRF;At=3D>%qLwmZ > zfzKyxWG8v<;kpatkprjh4IrS3%v3`;6+f|vt&X04yA18muj~%3hJ^)FR+9S(sjelq > zCkx@*E+9CsQrKU}^}KhgN>VIx`?|lwng+HQWOB;mP9s_ou9dDasTup*%kwoX > )z(Tp > z@8a17M&3E$s6ceT;Wox+1F%T38h2zQN_gyc&y2|UuRhYz(s~5JD- > jHBwcr%20bUM~ > zh$cT}^9$NmD{MUl^XZ=3DLEN;KLQ;zi#ftdjtuoYcyFm=3D1rY_8&ItIx95vHBNaA{qmn > z=3Dr7WhP0hFla8gOf&e89V(6sn;9X)PYGlwFmm8TXRj>&WCw5f- > bCjEDTbbkioa3AvU > zTgs{woXY3O- > Ns#)ve@9NMY;oo)@TE;@*Kw{bOZiQjK_TpSCkL>hiH|vnHS*twFPeX > zX7=3D;$ z{LDO=3D8=3DaZ!vrE7`_zV402>#~hF 6v > zoVxbdt@j_x=3D4EH*npm=3DH>2Kc#=3Dg!H=3DPKDkoBoGmj8Z?wfME9i#b;%s>uHmeu > JvQk4 > zrD$#eSZV|6lkKX*pA;b5>xpHS1FU6buU0vq^^pO t>wSc > zyzqFMc8Y3Z3y^#}T)iton^>0s@Wzu?@7X)?)~m>HUlce4|Af- > Xuf0lKfuTpKFA041 > zQx4+PwhHUFw(Eop5aMNtf!5V|U>nZxc8Jdxq0~JV`ZV_yIGG(;W=3D~~z41xdY8I; > hr > z0xwRigX4%7;JFoUOkV*vA~aP>^QpWwKysIeqY^7h2fnPJFq@#l*0rRB62iCn+w > HXl > zz&1Drp|7~EN?!zH5pKw!JvbXK- > ir2&;dAjelE~?bIwR>mA^QX@3@_RN#@q{(BC!KO > z^Kw41z{`{jeOz*@MO~nbPa1NC9aM$lQN0x $R > zx6k>}-^KH7O}X^Oz;Z1W{tHv=3Db$jNUE6_g6Kq&whi}d9N|7~jbS0lX)V2Ez*`f;dg > zt<&wmwS@>hFdJSzio$66vA+DsAh!4|7@aYwAmWu>IN1O~cIj{jgHjDYkQsYD; > >-$c > zI>%fC5si|#q%nyfy=3DQDYHlD2(wlilMG_?Og8uL6|fFm)J4*pIZ8}?{VT9 zz<`sNU+}&Q_;9IkO#HXRP*(ezDNfx|WAXdJ92EYtZv^y0HWyUT43rYRrhQdCOP > { zAUrl?x9!C*;&T6uk&i#;>va4OU>~<^g 7 > zoN_;)H2CRa0I9iEx4zIJrIUFA$l?B~Dbb!g;w-D*(Co$gzWarr&T|GZxrY2eFJJln > z1)e03ffoC@ZN$1A7G@+T*W`djc^vmzNgS}P)I{B7- > kO_`Up!D&o+Zeo37N~wiT81j > zNr?f%PSo}VlHLS;Y#<=3D*USqyLGhA{ znN=3DA!NCDe^oqvH%28f2Uyd7%uaZkVr^2Ys>xC>dAvB)UQZXPhaXJ!4z0F;- > TI~;EW > zjLg3{<^1=3DOSURc4o)1tq$F3IO3|7@U%5rc605jnM*u=3DZ9p4*%YI$Gl n}xK > z502DtLEG_6$;oyhKVny{@h$9M1&!>+y+|lC#yg#?z?k4Q);(Iz;dre(IM&i&ZOf{I > z{(hbv3c&=3DRo{mc+WSK$e`q_E5PJo(N* nlbj > zoFToH!G~jO$+R*$0{EKZ5dJ7no}c+>=3D3e|`+*c{`%}7ofE`3kpqecCbA@qY3z=3Dj05 > z{pEQ*=3D2eG|vbR174ZndiXxF^fhH2k&kM!f1uy%A0eDB%Y!0(=3DzChh$;$SJ>j- > Y > zZ^<2ncA4q&`4w=3D0et;bQ=3DH)gYpgAOZDHa`|7 7 > zf783)S~m*lr&qzajrWI!D5FyQzD#h;_?2=3DjV=3D~UmG>a60SLXOSu0- > O=3DA5+6B7bE#7 > zhYO%G@1@I%seHtnhBB?`=3DXm^0&$Q&{POQHR{_6<=3DY#9$FGe=3D&cVnE16lCJ! > $pK^xF > z+O2|3$2~`t*ayjjH-7by{2u8n5IPbEaSGtjv;*ifi4!LD1M6_2cdvA~_ksx;xntn> > z5mc4jz)=3DMLf|WIB$O4E$w|(lDU(nWp>gutdj%?qpW#npSx{XkkMK149>|g1Lf > ma;W > zz@ZBMjKA* xTr > zx}- > xSh6V|xTS);WM5GiHlf2YJ_w%RBoO|cobI;yq?X}ms > z+wPkcVoF*v=3DaByM1g84rO5BRPpi)C#*G`rKqu?D- > ^e94<`cGBaYaA!4zd=3DKEsJneJ > zd%}Zl026m#*+6#l9yFz!8d8<-XE)%+T)#g)iud?Uh@n=3DOKpmpN6Y|(vh9+~Af66i} > zC+}u&X?R4bW#^;u>A}>#7rrn1W?tfP_ukq83z>r=3Dol=3Dz4oz=3D=3D&;+`xn3L3HYa}{q= + > zD#6R=3D4Z52d(S5iva$jQAz=3DxFyN- > q~znxvNDf!zoAXOEXXdSb+g8BzmYzXab`Bop~( > zyz-&-#QEpff|Dz;D-}LFciJf(C3eq)rhsCsm3VhG(yrhi5{-gQu}8eT{~lcs$-Mpe > z!2+1otlTU#oN18nbcW@9kNZQhm4AjPe*an_%=3D;HO&gPurv)AyG- > UmfNcjAaLEpl$S > z9RpDikaN9IHWlq&X$S)F!OwSGKxcz3n{uLsvi$CFkH2TMF3?=3DhLzHb5@v!q{C7 > m#Z > z1f>)aDLDPf#(_xvLI- > F>Z}w!CMd+OmAeVD zyohoSxQ&Nj(RtmWA#w6fz6IuF(=3D3F07BS^9lV;=3D44vB(I?CQ{$y8ckpizR83h{1>f > zT08_N1kq#=3DB|gvHKTa?!R d{&70ArUQjSu>%=3DMWtVgeZ=3DP_ zjiP(A?OpJFZU3jaT!gt7Z5FceHs`S@4QvKA4(ZX+Ki)~jrhc^fOwq2(K>M_ar+=3D*S > znQ*VJeRcjMWmG>X0@{h5Za0hd(?%_QoeyPwvHs>P+ahRNNY+XWl))8MZ)p2 > %fV|*A > zkt(#|ggnw6k>TmD7Iw+bxp<54E*ZDLqtoLDfQwYLv}F|_`U)&bu30Cm(4M~S{F > +?=3D > zz^z`&dZKtALe)!mk|3ngTeZK~F^ifjN3c2?;X4p^$!MbUbw>pD0B74Xi&B)&(?}D0 > zIugZZoN&hDJvgzTn^^1- > d0xP8g)D2Y39)HEmFBx*n9LC+$6Enne6hXR(7}X&!_XuR > zwji**y!RiCR7RSsz}X0&@)sywkm=3Djg9n2$e;{D%a^9?Lv!HBwyyM6AwG7h@yw > h4&L > zyBwD(5{qU4{Ieig%`C*N9im!11;!?(r{9!cBXPt%l-+5W5EJWN40wt&=3DoX#4jZ$9v > z{*m|Amm*N%kQRb9$CCA7v$u~=3DGi{8u2vjN`&fId$;!qJPa2{<- > APOr43BM{y?@d>R > z54%cepV!9qbxYaL_3$VxwsjpQYsDA)_|(dO- > x&lC0KfJO|C8kgnlMCC`W~^izTp?z > zq@||d&(_INs@5efLx?qm_ja(T09^D5?Ys=3D4d~$k^IZ6mzw7Q%f7QEv`ul3K8rr$ > )4 > z9RBQNDiJioKM1BM*bHZYZj > NU_ > zKFLUvobQH+c7G&flpaThp}*!aO=3Dp=3D<5jeR7x!1UaAU}UU(skINr_8o7R|VMP?L > wJy > z#n16?gy`wr2f=3DvZKv}lf^K}HCru1h4S?wP^h=3DRYU6q$Ey&miVo+kAcBx+1o- > D+MC? > zdh#|>85E{?7W+5}$Zz)l_yk9(d~ZA1K7%WO+S{h*g*>?dWzmVU*hPmlJ6Fk#R > @B3p > zS5M{2#~%)9sHhknz)rDWB+CNsEh;lkgf93*+SB|- > dpiIGuQZ&)3%pWP8|}U%p;pe# > z>HsfgKRv)ITV?@@V}KCPz~Oo{Uex%q_cOz`0X5}73kX>6A#r_3Aq8^CfY*Jv^cg > Gu > z vmmdCBjk`SW)WF$ > z?aBHQe~(b-l2-_R1*rBPB6kVLrx_>jHRLYSlA)2x6A>oA63J8vYgp`QoAHRSCj45% > zFVI2lgreX>Yz+W;;HLbQC*N|(J{EfqY34Z{RMXnOWdSCwO|sbIIgpQR?;a;iXOh} > R > zv0N8>X&n57s}J8$=3D?=3DloeD9TH8n`mv!#sp0u=3DYY=3DD@kJ(;Z`<2*8!= ; > z2QH)1Ib=3DoK^I;`- zElSiRzg#gDllQ zkoIi}NEVla`Y#J%03LZcl9t&H5=3D8j~o@eTyndafltb>9I&Xq=3DuT)Y(|MxV}+UBck> > zT^ej#s_4_grqGvcdEap2bIOsxRQ0>!J0P0PiB0C%hFhS}QVHA4flL+Fr$m0MR1=3D > O7 > zlV8E`m7C6=3DYmpoc`2)xwm_Y8m&;4^XA{L%;PHjA+*8p!*ORy}m>c^;rx0S9`I)lJQ > z>Yj&UU S(O@ > za|hh8o9G32-h-Z-AwNW*NAs%Ok2QP2*64Ox9)PSQY`C8T`t5l-z$8~{- > gM3Y^aD&p > zl;~^w@!x;!kv?=3DRa(Rl?3ozSf%6b`YIaA+9H^I1NVW#Ejq09%Y&i|YPk}yQ~bS*C9 > zH`Ia9*4M~uk4Ij_N~?${@%p|ZzdekkLK4Evj2Nf~KnaA4Y|0Ff9b$SrH;CceyhPw > R > z^h+EJt||>1en#AMJ@x Ms? > zkvb6aL82Hn5hWQPxRz$B$AF6S;qLDfO&&S<7(3cmzvF2p?cw}7UL^^d9Dn6l;E > F=3DS > zx7~>-e-- > gm)shl0(3GlsgAmejhGC#BBI>?5ZaM1_Eo#}c?FDa%=3DOBqW^ykl&Emifo > zV(U~P=3D_ItAZ~9x}C4;7~8?kyx@k8Oz{2$(HkhJW;f74vn1@Yik@2{=3D2{Ky>BS2`dP > z75)y9nTUi^rB>ORx0b|5#+1aw{XN8h@3fUn2zCQ^cw(=3D- > P0RWsv57^A{E*(nxRVQs > z4MYp=3D@oYFek9&$~Q@(INh%!es_n@9+WNxL;C1f4Y(%SU^w#Iejj=3D&6{8e- > eJ(bu7W > z7w;{TV)#CXjaPSRBs!+AHcwx>59Vn;=3D#@S!^j#@wC$0>eYYjt > 9 > zAPqk0UO(`P=3D=3DeL1(v)qDUhFDyd;Ns4b9VIi@vIaaKrezjM%rua`!;fyW > zN^8AtCrC5< M>h@k1 > zpXYqySjjo}X1f zDw=3DeJdgLX}{+b!rZ}(36{Cel#mi816-l=3D=3DgLX=3D4>aldfS_fQb*ch+b+ysufdhRPc= 7 > z_a|P1mGIWF@Mp*qZr72GyYjv1QpmG^@@p5imtH$ Z~r3V4w4#M}^k > z*sj14;|gG&Z*P5kFM&v3b|T4P^Y@ez)00H27jC;b-?TRmppN6!(7- > #MPr;?r3h8kN > zoY(pgHQ~c^)OwIEvQz#ficg*l#qP~Ps#%?{wfc&8tl;d{(8CDzq`b=3DKy!9jROlpRF > ze@vBF- > ADj4uHQo&NIvX;*f zg^<|=3Dzn+!lvL!_G?1WA<<*yHjQTd&3cN+AaQhm8Ef}~#Ytl&Pt^qz<{9a W1 > zUSNOO+PoE*&!cG@G<3rT5WLlJ!5-IbuhBtqk71Js- > YBZ0Dx|q=3De&coqf=3D{1P?*5gM > z-dO<=3DpUw9Hu!6VH- > $5kc$7z^(l86&cou0^dL+lSn85gh#q>s0CPdPw0Sv0Ysd{^P$ > zEQeQaw9y`amUk_l7>l~Wuq>tjqb-v6aAw_F@Mu2VZ~nkt(zE#_- > Snz)c?Y7D?W@}5 > zALQE7xQ6{ HV!HNe?g<7s!)8&&Xyxc8_Vr > zQn_>Ijll%T9KE4fFPJG}da^_nVCG_- > cnLHyICv;33^$*4$)O!7p5{sYR*wW?_&GUu > zWmgmr?$5<5Chy_>jgZt7gNn%cy4F)Wl7nyNLP0#Onb=3Dy3fHhrsPxCC}DM;M% > 7_V&j > zP`t;wRsXO`RJ*O}Y|nE)NIuga{6;i|YENaCxDEUliqggU$_WvHx2ZGePQ` > z_z%!Hyan$tO9S z--mu$ZE#I*Q8<#GJ~- > LS0aHw~ZGR5KT#dA!aCWFP%z%Bav8uqjGkP*%Bh%ts5J#$S > zOYadBT72WA!GC3=3DtD@j$Ba)2p2xry$cSJHzu7$XrX!SP^eob+%?tI&b`- > h@bk9O3s > zjuA$(co6G3v*#KKcgE>Al>%ExH&Rw72d06 y+h zk-Qk=3DflDo-RlkNspn2$&YL9a(SdJi*(vXb096x>?O9?>d z+C9W1IPvuo^Z|- > GFe(WvK&Ai0GvZdYN5S5d7xc6rPvKFZjT1deN~)19(Y@BDJwVT0 > zbxupzVOFT?-&W{fd{xxG8AXNk%7Pm94u~>&-wMCy- > t?t~fVXzqFG>P(y1EoKnx2hb > z@+&uw79B2$lEJyKdaqniuztG&tYA;|i(SjW6s+UZpK#d7dUzzY3C0B2Tm@B +mG > zIxBSp)dg=3DkbAkxVn)>Z^p)^`N=3DOb@Fj7RWWLBp#pb2OT|Ntb4C6aB$zyH07+D > !#34 > z-*FDsO~mox-k$&1G_Iz3UxuZN&)({9;W#USIHlK+idW6zT?IGU`r_MRDmw93- > 5v$t > zr>|QZQHiRjK9}P$%aaL@7!*;{h6rJ5!=3D=3D#&Ph{6K!b0E&oP=3DJ>1V2dUvuK+0fwOC > E > zFS>{MgqHhV_c))pq*2T1g< Y > z)aDR~xD1dzXmhus5Q??fihY8I- > ?c3W(w?W|x3%hfV>F7NoPhb*v0Y%`fH#_Zr1QMq > zoxVl$2jlqyGo*@i&mU`=3DkE>!UkPQFUwe3n?pXg;yCVi4n+qeFLo$Vp>IkTf>n@ > H~~ > zO34EEbW0F@>jx)KIesBh*b$Kt`dS=3D(O9jPIZxS*OAm-GaaE{BaK4Re4CT- > h%26ZX# > z7~{-@v;G{8Lxo0@cHdz4T{- > P(*hu zXu9U%EAe6spoQ<;`1tH(k++PNdQ%ljDGo Xr9nR6 > zUOI24&qF2&J^gHpB;Pll0SAnRvA*geS$zit- > X7nvR?6z`88tI1O>qbo+nQS%&Mfw^ > zZ3$IK{RGVZe9g9- > KhwUi{&~5`XmT~NvEyMGDI48$`@}|EmgHF*YwM#$x@>ulqu_{W > zTj#6IYv<7- > Uh*BmLm=3DffEyZsa9rWR+@6XjDym0Orb#pr9!RFL99pOKzt{N+k7(7AD > z{ZzN7#^JAPYu-X7Y- > 03kZ#hrx0^_#A5wW<#y`+spJ%W;l*5G$@vWBC131ePM{uPf9 > zty0i8dQ6vXWVc@yWWxqY&sGHu_OZ78lFB^OBJ(k8zQ=3D5t{hp~5w_A2#n9b#f > 4A>iW > zmneu(RC2$=3D*qOP%l({AU1d- > R!*0ha6cEaR!vSFZB|d&( > zM<%mKuCj!INbP{7>M%j3tG)<2icbxinu$DwxS8>O4Ec>;(h)D=3Dt # > zJ(vY!tu6mQG%Hs@<~kp@co? _~%; > zlikIb58TfQ1Za}R{w{zIFd@CC4neH7!NEVn{6^u{a#L@gPhzSVUO1NUSv`X2KiM > #a > zLW_Yy=3Dn797!4EruIG+=3D;=3Dn5oQt58suzw?@(TDo90yPfy%g?ME5>*#_VG&7D > vAn@ > zgFgaw{JUI?+%X3J>0KLZl}j7jX zigZBJ+|7M1j$2mJP}iBX##!u7Pk(XaGgtulI2B6c5*bbN1(cj1HH&{c#1SZ3ovf{l > zBA1+WomJ(!CPlz`XlEIh``iMfF~Ay>!+!X)g!Iz(Lb2P}9lSfJUdmAoA!O%sEUH1~ > zb{z; EP9=3DwZkWSqwPf`a`R&_k|@1@ > zXh8B2JK;2|sv=3D_0{$G4L@&UK3!-U{zu@OAN_~(y%e%EL!x>_|woELo^J37Gj((|{) > z(F90=3Do!*u5jdVph%_{!%VWp599<;O6JZ5J3t%c^RWX@#axc%Y0^CiyH$- > (zx$a^1S > zX! NjNKUGnquo0{sQ}?yToJfw3*h&uvhQG6PN*%g| > z59#5yBIJLyDJM+gu(IE)=3Dxtc9@+z{EpvDk?Mgy+456}IHtptMRUAA9ZlJVGk`L7Fp > zH~c^dv#eL z)rN(Gm7KRI{ll!BcI=3D`k-ip{^=3Do8CZeyB1twe|JtxH6AD&P9gq5TCM;)g?KPkcsg% > z%bMZLDlmkuO|#z(!6WQC6x?ex*9oUTo4E92;jz- > khs!4b#@5QjP>U9%zLiUFG~av? > zeUJ9-Dv~3N$B!appHtL@3- > IW3=3D{m5KK zf=3DiP*`e>AWiFnbRq`w<>@&ItLppgBY#5CsjAVJycTu|rbik=3DBdG@rdZeR|{c_$*y > E > zHUIa#%>!W6413iHEGmpp > AB< > zZ)mY(gqv-5i{TwbtIzAs=3DBWgF!%q^L*AMkFZ6}+atWj+Lv z(AQQ_^zTLT>Hm69Gh&`4jCVb@5x8N4uyxEnmzgi0!6!s>fosh|oV6}_WA(lJ44v > bV > zcYzXTDySKcMWR|0H12i;aeQ80YD%Y{wq)ADT- > 57V;K;+syy?R+C30w{q70#TZ1(Ev > zJPdTtN2ChL+b91B_KC&23wNNX5L%jZxMN}9m>BOTf$-e7axH8W5dnJ- > 9=3DV@y@+N~R > zS%{uzSs`rlZWV8Sjor|@$P;6|-CjI=3DoMAobz@ zm&lfL&dzMVYV^fNL}_|Wtcaf@rg)||8zfmy%}@p0{Bz7$r!RgP0nAf3A0zk4?k< > >s > z504mry9n!$&LXL_h- > aJ?L6Rn4UzYN0He^xW(b$*k2HX19VtydPArk;;nmJG+U > zk`CI}z(W5j(E4&TMZ{vtdXdU8)yE5bdsy7^Sj`eS?TE>2N2*At7w(zm=3DV!8c?%|US > zoovPi`hU#(Ep@QAl?>9DG#vupyZ56lW`%kHRW6w>NC%_Zb>J~=3D`2U!mmK>Q > HH0-&U > zF8f znb<|K;rnYh8iTPjt|uL?t|ztl$K);fcGdTMRQ{nf+E&oOo&@Qi`-+l3fo4V!+YvQt > zlIX6B7;mNRZ&RWU&&VxHmqm+xpEf&vtt)sf`HHG)YPamIJROn5>1Vcu+_ ?krc > zgN3yl59`+tQ1!%a4C{8y zcLzZOR#Gizw}aSWF*fP832cFQ8Ip(7#2b&QpRc)r<;g)Ssd?bv$8l+v`ZKv>s%Z(| > z%q7J*sD2G2>U}4g+;R$x%(QvS@tM2YDy7>t1uw8rsAeJJk8aiD|5DEn+^2WW > p_`g? > zQGeV=3DsOsh;Zt}Sa3T^_EZp;4;9!*!6Q}~ckV6`unv2H!4ff$b=3Dz3Y!C(Z`S{S0W9^ > zN|zA(6y$S0q9ar#gj^lzZ*}u}{NEI?Q5njE3X<$~3$4RIKsiJtx=3DKYtzf(Tjibof} > z|JIgYCTiTYgD3F-tL~w=3DsT6P@QWXTb3nuNcbzg;thttblIZYsmX9ji9gp? zXx*n5Jk?gb+BYfhE0^&5w9~$uuFe6tt|qXuEdHo0ZOfA2AAVCZxkXv6|4#(H%`x > `m > z!Uro~!$T;;d3|AFH}rbu1zdxTkTq5{Ewxi^VJIY=3D0G{(35HvQu4K@xA-cq+Wq%`jv > zfA$w!wD=3DF5sJZsF2hr;}hOgoi>6@o%7n{pD< +%|*Ny&A > z4}6+}Q&aHD%2(?F6Q0BsVc~DztJMQTo 6k{Fd > zW(iB&TPd>0os(2O9UK@Cd#OPHA0yWQEz)ryU?L!%#Zx}oSEAf<>5- > JAQI7W$CZ<;` > zf9c>+_}8sltM&1IU5PAR0ItTm|2;f>bR0NORD9o0U5n5wt{cph3u=3D7O^iq_Y$SO > sN > zHbWCZN%MgE>`W(q@AW^NG*I`CSr>qihfz+ILA5n{lG)Gl0M4&*d>A mz-w > z7jmvPV`0OsbjXZKjOcxvWr1H#_&E}emiqu_Uf)}t5PxcW>oT9#*#`;*IyvK@=3DXR7 > ~ > z8fXrH$js&R?+S2XOzz1BB72FzpAaj1#f3Vedt|Y1FG9kMaF>xkG$9JvV8%B*wx3 > D{ > zg6^bY230(RP+I!$;>wzuiv&|PlbqO7tcX3v$B(sG=3DWj)e+#L#cU%Eo@`BUHvm3S > UN > zl6K4ju+GMO7r83Itxx@$bKM!Btcl&<-;WKLqS|ys{;- > XXcn}h4PYWxnsEmq(kHsmu > zG6~c$L)`73v45duhb!N$(**LRloM_w Nu2W > z%V|k`pVQ}j^|2>D2u@fe8~9kqhc;n~$5EGt$QTOs&clBzTTD?PAIW#W#K^^JkV > !}} > zz+g-u4POKg2i=3D6gn$pjGyjfvExvjAz^Sxk{nfzBms0F>@+xZuav$Mu2LbzjAkqV5y > z^6Q xL=3DY(GRx > zv?THE5DXNx*)?S`KoEum-<3gl6J+5Pf0(L#PKCqDIC5ciurEPuJ(ctyc)dbN`4l^Q > zK`=3DSF5~X}+w3OX!SIIcr;y~GO0x2 zz XGy > zt4lykJNWm;eFtilIUaypz|u2#D?d$Cpg)MCob`Rx0!bR0S)Z1FBTc > zd!7JRPe6)(WWX9F0Qzec=3DcjC~bR>j?v|nY~tRhm|4P;@Ll9LZ(%%$q0pjfsc3_QV > M > z5I|FzgC>bj{V!YFUb)yCX5))yG;GY7vAn(Uk^xK$0vIm$gHV2 zoMvmxOOlQzK3}It;CWsJnUUy(2WqjeH@+6?W6N$p%)EPd5%&;EdWb)!$f#E; > iUxRR > zQIUF|M}EDp(a@A;4YjqqKifI@rfo7`F{1)u+rmrYe^8`+k4B zkIoZE25eF5!0d4+F0q;Ehg#xKZv> zl)>9gfyjo`23LBZ9N_gO8k$&5QwF?gFI@c^Qb!slO%AvWg(R4woV<- > rUD)H9&Sf@E > zN$ZzgivaUPhg*}lS=3DK4+AtJ8yem7u|!M&3X&Wxn> > #iM > zCb^F;lM49mIvRj~g!FUgO{726TJ(nD0#a*k@KDyEk+|=3D;FHi6EMdHD586 X% > zz=3DvgCB>5ttTiYM1kvAIA`v=3D^YzWeJ(#~@{+1jqsU+hz*;Tou{T&C(J;s; zy7CM01uE(1=3D{riSJ|;IaWdrCDEZ;0?4ly{mN38y#&(RcU88Z9L!&P7;sNuV`Sc_PP > z5bQXroyB2&rvIT3VUGSSK(i}@#+05}`MMH|5C6d?s7CLM1BL#`fI~ch#}Q298}0 > gs > zDdH)V%Kl2OUu2LRL5kp@K=3DnzyyjkZH6v5+c5f6vE`WAD1Z+MTSB`^B0LMvM- > rJhB% > z3#7(Zhr~nQjon-9`4V!?co^SKsnfd`T4-u=3D)Dr#_X_eC2oWgpZr_27sB6FT}-q63k > zPBTLqcmk>G0PwmJ^W#xC- > O5{4sh)oh4iRrit%xnJ{DwQc$57&V`Jz(x1UYQm67AvC > zvp^ESLpZF9eROJWE|=3DgL?9R*pkXVH{Q74dgYQ%~j*72f^EpsrXK2X6S+iC6)M=3D; > ?! > zUa^- > ~FA~XpwPIPB1sm1Biok#Q4>u4a)Sj%f@rDkG!%Wb)@C&iEYf$ZU2D#*%J?t7L > zePZN$v@;gl%~~;Asr;aak9h4#KVRJepJGL8nPSmJERlM?{&@FR1@5f;#D > K(~ > zlG!jYkgf(6?>=3DxsJ_D@Vd^mTpqE#nv*En7u`Y29+r{Ptq+Z?u?+BNgP zagSCF!cjqyCx>NgC)?)=3DQ4*q-^3T9`SOY49BS`8}=3DIajk54^6 z*w1fD++$^@e$D7^UGJgf- > rz)o4yhXFA(G2ESv?9D{nOc!k{JEosH_eCaAiqWAE3 z>w61?egm>^5554QB+EaCB_1LA7iKZ4=3DhXF6%HuNM54xg~L?`K0d@4p- > @)*^N(5Ei! > z7@+Ss#o%>CJz^!v)qOiM0JaT7eX4_Dgb$-P?4iCDOG;rSwt)!$|5|d;6?lDoLuYu~ > z?j?iN(OdG8$LBBl&x&`(I_5sN4&Es;C`uP- > wj@j!4#6iMbDW)MruAkHCb1NqxwNM{ > z@bUD|FW%D ;P_l#tb4g > z9h=3D}!k)XI*`h<`4ePbpEvx%}w2I7JHiDm2|Xc8vkx|EjGiy>by&b zE6g$VLK4}BkTMeaP_gEMZ_)6o-I*mh57- > ^*tCzH+>d*z@3AV|%h%!1NOg+zH>^N55 > zndZA%RmeQn&0f|&dL7<0+QaSTS+lB8G*Th{+|@N^Is4G4$CZP9pX#B18@1h > E5}(1< > zY;318=3D%V^kx5T3>5LYsvd;WLN4d@kuZ&FaTPk9cT6lNXdw$%1hz=3DPNg+(jm+t > @BwZ > zL+pz_mVvehguWJ2OV>A0yVPCY3pOV})ulPIam(*#t > ag > z#{*HK>Ya~%jP{k&#i)>)fH!sv4NFiS{`s_4t)B-*P;RS@Z(;G7zHk!?=3DT0K^ziXrZ > zUY3kuDClG>NPztA-S)#DE > zIt=3DA%p`D+5lKdwF>G +OTZJW>x%Vy > z;m4A?M)+c0g~Jf!fSmq$ocsW`-^k64xa*HfN`Yx;mDU~=3D`V9d;%3J@E7w~}!Pz!Q? > zL88?K3cUb=3D)VMSp4>97EeC*DUnL@g?gtsHYzV7RtjTxAS{;z>H3r6P1pLutS1cW > VJ > zb+H0B&jVwwptORM&u7~n;h7EfznWaiB~F-L+eYd^tjxO<)#v- > OhuXF&2LmRT@L_3c > zPj5rpLmZR?wWu4V`GAmt7^E w?L > zeXGQ4z;UA6g`eit0& G<~n& > zIQ{)n?35@%(NbkTE#1JhT{VVz_|d(y8#%g(WS}qUOE|RdKI&k8x?Z}MQxGniUC > {22 > zX- > I=3DgmowNQpLB#ygiO!yd>n74F?PBZbXHxCGhtc3*2`@V(6~cZ*4 z rrQW;e&dE?^k5LT>#68x!tTXa8gY1^~)(Sm5wQT6|$UMsbAes#~ > zqZrF LOb(Ih3wFV83{7iTAg=3DIpUFc#~_hVm3=3D3tn07na6kn?dG1& > z^^|5kQSr|rezHIRAXcF-BS+{T?ay3zO8q8+F>k>U_ ziH;qvJqf^A*l$Tbi z+R}UgjZYh;q7$!&%*9ikjK7uIsIA!;uFzcLu(q}?ezCf(q*d%jWm=3D%a4!Y}N{;{n+ > z`%vwt%ljnaER)E0o&0pqf0*pj%Fw$M8C6TWzQ77dQVW@NPBizFv?7hfmMd > ppY;|*f > zdVc@~A9~wr5$E1you*)8n4i?F-- > }{8m50E4mwC~;%j6|j6&yjC{Y%+Q_m=3DK?l&wBh > zSI%Y1q?=3D~m^Ko}>d_;BBGI<581w|N$#%R1bQ4=3D3rhget*I wnE > zb+TqV(p`0JZ5RFk9l ~m > ztd9}HTe*6VE|AINMm@c4XKX>Ef>z3q_B(2i4xIx?*cG9nTwIpHxFqv+&;F7v!!RZ> > z2$)#DvCaNsc3BR8? WU~Py7m*zW(Oj%ZaSo<(l&sCAH?+B&MMf > z?ymnv&w?m|Kj)2E)B8$(XjTD8Agzv=3D=3Df@O@_3mdSTvH)1RhwJT8no`Z4rhH)| > 2aiA > zWZy*Wbbc+_=3D$^$<$Au> =3D75zos > zT8*pKrE=3DRR!eAja--^g- > QOrn%{ > zaxauQ^(PUXSRbR@S;SQDJM^?xwLf&ydET5h*jBy(y Wvx > zHz{q5K1*57NBbKmxcCBi{EvCHGS8$_Y9{G?lQe`U&Y;_?NZoojm+4- > QLcYX=3D3PQ5^ > zc<0;YQ@6NkBm_m}rKNT#%Ey0V8oWGsD>p^`_^2qz9LLzMxzO#+vcjNME(QA > J(0a~# > zkP*vI*;CV#&yW4er?y~cR%^*H69v1B2S}4L!XWtID*`(!^^Jmzh|jWvCxp=3D>?6D > O* > z5S9v5&xVJgI1o4Ck|%tb(l5@E)x0iU- > U82oYM>ZQF?N2%E?Wf;q$rh+83ThDN#0-* > z`8m8qy(o3QuX@2Qin7Ls- > *V^6^;52SzBwIH<$)ftMQ?Jgn^smE^>{H&zvcQCDNuPv > z8A&gM+yWoI3+p- > KjRJ%+a7bEng~y5RdsNE*7sftxV=3DesQ1!sw4d(=3D=3DjqmRd > z!#W}nhX7W1B51;0`&^a0G+&&CTNl9u+sS?~H{w>USBcD o!Ys > zBRD0vRJfswbn+&cJz9Qw=3Dcin?OpcKVMGNMFAfM&O8L8!vm6eq*2sI%=3DJK B7X}h > zVR;Et%eD@V5- > g(*0@b7r^fZ>O9al$Q;v?y$%pjfj3V4qwfwQ&9tUetOZytiF2~3#N > zMhB7&uM=3De$=3Dl)X9NIVfawxls{z7llFuIqs`9}nNgTK`0_EUpnvY=3DIs>`x^s!qrtZx > z-w}-X7P!UBG?zlTG0_*AaDsn}- > 8k0{FELICbyZM7wlN > zHgWxV%B~vg3Kg%{-@*F@zI^eKA{- > JV+L{VqEn?WINlMzaPP}6rzQBIHhBOktbiKo8 > z- > ^T=3D&;KD $ > z+RL1up1Tr*61ghqa5=3D6m=3D6tz > =3D > zivdwgYOLA=3DA_u8nkGWY;Gr*o$zaim$FrQ#{8)}~GHHLSGkMp@6#K(- > dcR2oeMux8; > z8#BRnAIL24;hZUVX5|*DVb5}En&DxMgLqyC;&2Y%rCp*;=3D > YK > zjl$=3D}dcQ-TclI$!kmX6JJ_Glv8#q+9X>=3DHPd?eoIc&{e%%(NGC98ZdSn(NkO0$*tr > zj1yG4d^}WbkWRE46^~6ITd)!F{XB{eB#wQ$k}pNiz?Oz##q3)}CT&C)goUZgG|Lj > c > z`gTD*_OS15M#EuB7&nnf`&X~QFs2obvK{pn&Q4}pE06{_>EO5+OCR;u26_lcY3 > VqP > ztvy`MJ|o+EDjaEZ^?!a|@t=3Dz!=3Dz4+VIYT6Scg|&7e+gT1%U%YD9R~j+w~zOK8^b > 4_ > zf*pC~cjuZfQ1lM>6dKV>A)wR_66l|d^)$KH$?goz#BeD}uSZ|so1_T0S`eJ&J1{}k > ztM0nPbV%aUzR5=3D0YJb@0mFT*G{d3`zG+|`AW3tZo0g|*Z3}E5p{|bL{0o#boC6 > Q_? > zJ(u1|o=3D@wYLw2lnVHnw^E|qj)izg}%5+9SH&oR zcVybwMfeLd`3`u0BhCKnb}Vkp?u$TY_k@-cYoaK>!Ov_lpGNxogd`;-zV}=3DFfb;y0 > z9ZLyNP?CoW5I?@cS8JHm?Pnue- > 5xI;X87CbW}!5bddY^J%4HD$I8uSwf~cRJV9?w% > zG?boBEIik00OG_nwFt*~unKmgH1Qy{xnWJZY3fEoc?H>u*i#4G@Aaa+&6u9g$ > `2Z9 > z+y{IlIas|V=3DiMZdaS}_c@R@Ee(G0X)`dtw$cYu|InpLm4=3Df~mNJXQ5ssHc2%yKM > Qo > zu9fa8MPV$Kwojk|juDMnawUc7wRN#3#aM14RlQ13n!AW9&Z=3D8!Lu~&96V$# > |)V;RX > zy()ibQIpP$4%A@T)rt@L*v&0q3O?rVgL&LoLxGH9a6`4)r1v&zBD_O{v}6%?@O > x5H > z(ybnl^bZz3Xm!6(bXg!~#b@1^ VQLyD+lkS|z;JLAPyFb5uzH(MgF&C5c1BO{# > ze^&A<%R{|w5J~BpH_&6EI_V_uh`3EJT6 E=3D{9 > zhH#!cjx>mDSgH{1t|kWBrVxvIidrOx{1T;CPvCEyUua{ > h > z1c&pk*C&s-u!f*^mD {0H}AU$m@ZA zqyCdPYA>=3DYf_ZfXdKIQ7K- > BxMdz0Pe@#(mGkjYiUG5 zEzngQd0#zadN7iuH;3qUV+H<}R$Zq#W$W5eoD5ndHwjz-- > =3D+yEpn0V;dyO#f3quY* > z3?>kn>Vi)>JhEr$6_GcDh+hiBX|7E=3DLA2o>ZNDE#~&;jOauQ3BSycMN > n#x > zT*EfyUfFujUlP@;#^Dqw{UC*}*;4vW;(ba}Hf4@ATiCi!eqjl7>NDX+N(i!&otS7) > z^NNrXGDkM=3Dus)*52#FZ<&b^mg``(Q`vcE&I*oIXp!||tb-o=3Dz*{Caxp- > >DBbJFmh~ > zC&kb79A!d^^#HX$G5^M4i8;6`x+AAAkVEK|H=3DvNM>Ib*E38cr3SZw;t>h 59 > z=3Do42)r#pYdfcU)y z7oC{H#>Kr9jMo)`=3Dty)(Ue^42 > R > z8HKN2{FrjbtM2HXS;J%m0IxgH(;$(SZ8|B{W18=3D33pp;{u&hhqc8+p2UTR- > }t > zaL0S5=3DU6RTs>(LtVI87-%xlC$S>0I_(e`>ip2AJ}rD7B zUDk8$a~-gZ(&cF6^1b)~6WMFJz*q^C>M8-7E- > c!f2kMR<>)RwV=3DhmWjbJxmBOGSp> > z=3DzoS%q90`C_3r-1I|(vxy$L|<- > ~Y7EfYmHdgT5<*ljzmFfRWBdcOcpKr@m}C53#E3 > zez?fWI!-qRxJhVp*vt8x0_kqK2VAflQ)7I0V$gwR7F-uEorX*~30!R-Ic!Il5+a%o > z^tr8{#p0PHZFCUv1V}^OcI%aFIh@A%Q*+t$VQGcZzAm- > eR5re6n!d6@_6hyx(I78k > zK;UiB0LRe@#QoRvPw2NW&OBNm`+FiZp wWe6|P1iF^JJFS9FF4afYWssf9! > zN2zMr)_yOt7`nX)8)P8--tmUX{?jlKrsl3vS8tX&yD1HpSH9o8odxl=3DQN?(iDZ9ZQ > zyO&QomwWOpf3PiC4H{QtlXOqFMiCRG(tTpdY(fV#4g*)T?wT#8n^=3D0^e2C %+f< > zJ-6^iFrz@vQpI+-LxmGl#el{p*vzh9 zoq1c%NFuDMDfg~=3DM#_+D_9LX1kR#*a072xSHz!4s<@gk*1vS)BhD8!rlp1PCdj > +el > zEkxc;8(2Oj- > Ah8RUUz>+$y*=3DCEnVHJ^+u6etFX((dn#Glui6(pPU@H*0yxMe{G%>t > zFZe1N(- > Z;#N@dG(KLJ|u;*VuZ&D=3DA2hOrzEt&(5XNVC&{{)UH?&$cVV@zp@%Il^r=3D > zz60g?JX+m9?|5%jbW4yUa81IDoH^Fw31Y}7nBLXh- > P(9ByC7p(0M!&woUWlg6d?0S > zZ!o>QVuB%Dbd=3Dtj&- > _T`OyKD!hYZX!DRC1EA`2E})lK@`i|XOa8R+Uak#d`<%c>}u > zluRXVCM|is^Bl(kxm>BHgZ#O`F<1qmn>_&``@91Pc~zrjb~SK9UlUSR%6xtXmlh > +5 > ziyK;;^&8H9c5Oz0a5WBF=3D7y;g*vNQdHa{xg=3D6uy0{{G|gQ*aT@Dc_O;%HPC?Vb > fpO > zZ>10BP`pt4JBHrFwawsI8M|4qZy@X~$%w$6$8j1fcZ-#aigr3BUZ}&W-!~C&zci|? > z%tkT(e5v)Z+lp_l*@|>YJs>6Nl*C5TuV>w5n61JO3HFlGoG?|IQE7s9J#@W

h?j > zvWOgvU8>uYmO*yde#hIZ_H;mtIcX68LqhJtmB2LQ48 > K^U > z=3Do;?GIQang%^L1x!n!p}S>PV$WL^gqazvSc{BG6r)kaWWEZLT13sWjnZRrJgu)+9 > a > zdd~d6ooc=3DsHaIM- > Wn > z9YL6MHz1*R`Ebg>i(85{!WP- > zs6w5xDv+y~l^pg({47V8Y_>(}r=3D7tMSTDm5a?M$< > z+Y;_nb+@OJmAkU51rffCLI+;|jKNav7|4vzNDvHE^4)S-!6ziv6r8a3`J%{VBg~ zTiOtjAgnRkSwGVn{aCU1iJ|6eQ(#Xo1BCKo%jdg6zLrIblf3d`p2ri)+OxS5>W=3Dqk > zjcnO0FC<- > zz3dhk!F)B%mm+28yOO0E(6lB z>8e*+-?i^0Jse3<~4Ercs`NIK?TzdG(`KwPL3%B>uNS > z9O!gMa<;zpIPCD!-j&r`_fN+xQ%22+BKS(0*a;%S+Z27$!zP|KUhe%$fhP7@vb- > KY > zbj`P6wA+`MfunXW6;QNEU#;RNm6RLlt%qc5>ee@b_U)Zgrx|5J3Y{WLszXe@){ > }ye > zL4|6`_XS=3Dsw12@SBZ8G9E>&n_+D5Q*Aw}2!M8=3D_Po7HX > $b5|#j > zIW@eDS!G*#FZ_aSzM6Whi+MFQk8+Nk!eqN5C7Ee- > d!ko~1?$I;0XQ0Lcy}V_TM9HH > zb4vOyC5CU3^%O80(UfpbF<%Kpy?ccz$FJ{i&5i95D5M- > xe{E>sl9(>CA<_JNmMixd > zgZ09rG|8v<#R2^cb_Q9&O?0>H@|fZ>^&^g`yP- > t~(*7E z{xDU1oVX)9MCJ5(Oh47d$X- > XGl*%Q0ElH2aXP)|ASi9~aI8)Vn > zNXI}^yA`QVd*Ix<6~QEJ!$pU`andLlnaOpQSAd(Z0|7YYiRNjps7o$I&tr#X;&99s > zoTGKSc8dgS8e43P_6{Lv#+DrXczsc!YYh_{R($sR@E6L}dAWF;-*c4|rL zZi@JMomr2LTMFQSGYSkEOyY~nA?n37ji!HozhEPf@azC{=3DYTSKqRfEG=3D)Ue<^$ > AaI > zrVq#GPwAdV?gUo`M > -hX > z+4vKEX5<>%BK7vI13ip2HVXJPg!6CZNd4p<40*` zf;{O=3D*hVxSA!r!WmW~H+xk > dv8 > zYtFMXdWlTm4a`t<&@F5lJ~xH`({Z^~E*C3@(FyZ3Ih)9c<~F5~h>1F55^h`n)kuH > Z > zEo}_x0p>=3DEYVv z>>-{a--W&ghu!f;H9a+yEq=3DDij1l1{snZleF@7? znAko2I^ryw^Y;QmX^s|53Z`4idEUG81YOG45t)g- > dTo#iWkSVwCW1XNNk>DTgaD;g > zD`bem(%+|V42`{asZ@whK8N=3D4f71|3@_3n<_Tw$}g1*boZ@N1s$8=3D_TxMaF > WS_L$5 > zG=3Da~wZEY%8?utBrjGUTk- > &zLAxDgQMl#h~l)AM; > z-%Slj!?gRjIQPl}70WP{Ms;*9D=3DpFnC4N{jSK > zkq<%}cHY=3Dlvg^SjCEK4k)PnAz0;TMOm- > $hEIpj? zfhE|kx1v zMBQ=3D+^nX4DpIxQToyCObl?wfDX#BpYz_0uKxn7PqvqTfhxrsU@w4FWnw%Nf > }$y^xf > z%lnq&wtt!x9WIPgV;Nr+&^{sM#l4ey7r#^p*V>Vf6!%ZtC2I$*^ypz}*1z`(Xyf|y > z@#FSJLX_=3D`0$h$|M*-arFk=3D3l?B85Q7bn!cY=3DW_`1%DO!AQ~aUBOH~qk- > N;TTN~tt > zCG>(XDrX})>@iM86gSa1TWbfBP`pwh9j+~v|0Z`Acg()tIXjNil%xQ^EfVI|iYg-o > zQ-15yS^!}n- > @U|TXVfUMwRE?zl*_eVy@5+O#6oE ztMK2DgyDFjD;8tk{8P$qR0N)^qvDQl8bq~A- > Rih9xWf6KdR0%$1u!qOps1C2Q#ANP > z*c0Wp^%_u! > Dqgb3Ca > zwZ6y<4qiE1inH!5)^X{6;`c*!EVGK_+!UWAPC-1Y#y8n`?mD$bR92G86- > gbBvpzXR > z=3D4cOJ8e)q6Z^~rSRcCH4P;nLm}!0wn1v#a?wk&_s T > z>&z`g+U+&wQCJYn<=3D7X%4srCIlNl<*F97oUOj- > hIrr3a)n*V>r6m=3DeJo=3DM3#L3_&Y > zi&|44<0`bj6vbFuZHvV5v*Tl}Y6wYL(zvj+oMpUJFa7P(Za0pA*;4gpG2kiPYH1E7 > z%kHS*0B_EQwIl?iLRNt?r?UFu*P{!%ZuQJ3*bEo%s$XUu_2Uby>k4)`{|H5- > 9)^=3Dn > zbt8pAG%9K%504kW`h8@H+=3D5afvn;8#!)+&){MNODhwu^{o$g^olPf`adTE;FzZI > Jm > z{P+_kTH_9?=3D_3 zPNi 5f7(M > zld;FU)~fe8wj)+V+gAR{O_5W_BRp46;r@B7saVATOcImYtB8V(ux>)*FFpKG- > kUG* > zbj?;tk4&h8ks=3DaI#b&BATBEIZubxaINavi$G|Yo9Uu&v;^UHwU%+51S&)n6q1no > Oj > zMPo?OrRqOZ$l`tiC|oNf%}?6Rq!z@)U^bi6l)-2TC_XY^XyU+7`Q)it$=3DyI_HeO$| > zusUqBuynNX;DC4#>e%{hj6oNs@~R}7@6#vT@|duD;fh}- > n<&rw6V{rKB|$OgGoBpL > zIT~w+S|^qu1tXgO2F<^h>+l@UUB2Y7FJg`|raEdxf-;e_A#;c07yl@emICdlfx6?S > zXwrIe)tA^McA>|jZCSZ$>Rz^yadM4MxQP~atWXTJ35o0D|1H)~soVgEEW9- > WPr4Do > zbv#2m9cmSieCuoioKY&r`!OHR6Fb>(2{O9}bQ3;0RZ3ou6B~&o9L0x- > OHJh=3D>^%$z > zr(v~WRH@LUkjY(^m{+xUr~eBGBUc@%3I^@A0J{#A3ap4N$I=3D&#UXpv7K#RfR > =3Dj_i) > zvlD1Rp9-K-yt~~EQly0^ELAHo*pJNaq%7IXKmO^Ca`NmP$o$oBj+)}oy-pwb- > >N#* > zYTk}>cvf(Eio_RtLRn1vCX6aS{o@uYfKX%$8`FtN4l~F3jd8G1&21(_*`VLSbHiO_ > zLs>gSMg&%IxgXVsk+G<1aLQyI;+- > (x_K(boapOd*Jc8fmX)TMgV%p4d > z(US%&$&uu(VUmwX9e?UB>(!SQ#5p{6hzMH=3Dzo ?%3UXP > zmxkemLOe-bD%O81OOdPv56yFNIJswr)-r?Bn#M{;G zU(grHG-+H(%V#QFN~Lz1QQkoS96gsvl;%6W!=3DezfTVw6G&ENk- > bjd3)EC1i~W5H`Q > zGcOA(cO<1}5GkNlakQ{fg~WZMH%T=3D`CyEAbOg~Fm(xiAm)|Q7QrekwV`Ze=3D) > @EPJJ > z!!F00iIqj9 TCZG5;+vtU1X+rNJQh(Z*U&T5L;8_ToaxQw!Y(UdWtVDBwuZ > zA@6GBn_*` LjU3lLmY?1#n};Vy#On}i^zzYZ0>&UQ@PEmHVrbJP > zQe9b%lCpy*p)_BAzok`~)E?YU!gC4r#oRefnWc4$aY)4Ljf5Ik(p7%;yAoq*at@SD > z__aa+baa$;DP~4J#*rf~6|%Qe3w!b3Z)elu=3DzX%FrfX5poZT~l_GImpz@>2;>%o > &q > zxj*LqEY-ZnACrceN-~V`x=3DvCl`91axJ2^za`yQzUWnpBgooE3ASOi!r*r>A?1M573 > zN&n5k9DfdGDn-n*utQW4V{X- > IYdoR6Nzvw4=3Dn$n+fAy4rM^z$ODY+t%uZ}HIPMq2Z > zzW}2RRYOynm&x|upI7lGNnweu4`hzNF9^F|R9;Q~Z`4- > Y(zEt0m3 z?)<(!sWY%0>+oTjC?=3Dftlv03%n1AAK_FdvVtXf4rMX8Ba9I0>2z9HNZD9nT8rR~i! > zhxD)y*ONxd(B~Mio(S3g8(A@`9rH`w&c`gw@Rc*$2KJUJw3^vod+rkYJMb)L(v=3D > 5E > zZ7J*B=3DC4k&@mP%m=3D~*ruDFkvyaA0ER;jAzq)TtQXc8p#vIhuSM^7h}%`Zj$*2U > 3cy > zJ+BRLW^=3D?5@2A+9kwqEZDD*q$6{{6SAC$$*qf(QCPK- > }p4>2#(NT7_EYJdblL%@Pb > zEgmO;uvUpvD#_kw3=3D)o5;s3Vo`^_Qfu^j1XKlV+05mA}suMuqZkrsVD!Q!m*W > T~0Y > zO%ysu!_Wfw9c(PPtxsz6!b 3EE > z@15nNNgk<;B7RRfUwdOhx%oUbKb2tgs{dP+9MYPLN!0C$>lDnK?NT<$c)Dt<{ > 8Z-8 > zek(ksJ4c?ERGlm>9F-HWhC9#UHBWNsjs;8Q&9;AoDt2j42n`B- > jr~cF^8 z9`AxK;Ec>9;NqiVd=3D&MW(yDM}#3a645pu_^fD4 > LVb1o > z3NA_i5P8ryCA7db0rh{6i;XAsM^tt9XFlmOEYgwQO$!=3Du)7Z4@X6og)oSdUae@ > x0z > z5KK~e@G`%hb~*L$l)N^ixbOr<_N%m;O;C8u(=3DlM^$f6eHD=3Dy7u?J%9g%uDunn > ~4&d > zu3N8P*3{*xXa3QpfR@CHrD3?*m1HX7wyFL&OxLYsueg50v#~tdw^sl~>moAu > G$}Tz > z#IEmCFn7df^>D7-H--ADnz)!4ImOJ- > Gi2|AgzNv_G0P0ow|yfN@qVhn^L^d+@=3D?WW > zmAH(FJOd6vD3{|N#S5xK_9owlg+7%xDe}$UhaLBF*oZr>7RlF4I-)fwRt3qD8qt({ > z BLurm(iegM((pQP<>WUPp#7DNn@Z43$EZ%H0`$R6^s|a(X > zlw{~&GB5Lc8s=3DuO&^TGv0J+SVmAik|XS^mt0kB- > $@AJqDd(YoB@ZYfK6vP54O=3D^o2 > z6PdpGrmMi@5~S3;K#!R`a(d%L){l+SAG_9Ill>ge_D%SXdTxul zj7-W>ZIBF}i5K6kPkSNUM4m7I?Fw!I6i-tbWL%ohD+Ci5t6O&DIkU|??ctA=3Dh~(;P > zV+- > mX(dzW5hXf;&^sICs^~c10!MG&Fr{^&cDSI!Rqlu*wn3RiGvP120c4^ojUjDmu > z6}$5tlC83L(lz#5@YwMZ*Xx%2*qezt1~rrT)Y)!5Ci)Oc@A0j%@ZRIFp 3 > zwPHdUsL_Emi- > |sooN3M0iJ3Tm+O~_sUU1w zbrc)1<0dX~X#twYtTp`&I- > K)Da>wNqCz?Afi2)fnw63HkT`4aFOl(*>RG(Ww1UuZn > zLnts|%Muf09`oNBq?MtCi~rrWS7%|HsA7H=3DZRgEF2M83}Vtrqy%0F$t#uHSG5 > +#Xc > zcWv?`wo)a$jOSP`u$)dQF@V7mPEM*$S@M!!j|+dTpUbQMFa!1znyw?%- > xVeiqO1Hf > z$va6h%q6r>fx6QR<>Y+bGN8%s)}YRyAu3&v#ihWRMPb?7irxHrA}FcU > zrc9XLrdI~)y6T_RtvmX9;c- > zJDKs^v)a7UWm}XqHC7z%+z)+u4)j6X96h up`F97_ > zo+0Z?@mXMsB>DjszoKQp;3>Z|3}kR2Q>A)e > PVQj > z(j5Z?q(^s1gP;=3D9DJ3Y~UD6$+L>;22lnO|fbc=3Dv=3DBhpC6d#>N#`>%6jyZ7AZJm>jT > z4)s@IGNTe?4-@J{iz-J0sXQl- > t%YTPY2a*NG3H5u4hLOwu;LksnSGLopxEym4(PU} > zuH?IzIQVy6Xt2}fD_YlToF%Kd=3Dm@4tHn(*2G{?$i jB > zTR(a9HZF}RoYohePw+nRQ88EtpS3fb{?*)A_OK%_LaBMN#ZqQ+3iHWlXNF>F9 > XPvK > z;rHKbd%7(+lZD{k8V&y3=3D$H^UaIlLK({{+)dqV0*LL;2BG$Cf@?B6cZu3YSx73ckN > zH}9#C6|qJ(- > ~SFy?946+aAa3*&Azq^XmCrx?qeHDVcVdsmd+^*9+BYn9ijX7;Xb5c > zmKeJ0NR1rj%NFJgA>QB^aI)cz7h-lOnIU!~3U4}<`5{!6`0?Mx?dEo&by z(-KPKDa)(}ain2Q`v*U1YavZeP=3Du4+t<5IyuN_mah$9j@`H`&+E6m6x3+b+9*2Lr| > zlDuhfwNB#t0gk+Lf@ThqGq3+^Kw?Q1dbge7`r|+91^qUhmTnm#XI8@J4NcUS > n)edp > zh_Z0`!4XH`%5vH|&;GYvo2_9%c$*5**c;S$a0s;|)X3@zq?n`aXEUaSo%Q_wuh9 > 4r > z@>}`jOdthbQuJWXs!`?*9ygKqVACy+z$FFT^?5zo+ztAb9TG0IPsiM+nP9?6qGk > Q; > zrZ8h#Uq@3nRJjh3P`oH{RV1tYz+Nb3%py7}ZS > z50i$04(#`7>XA&+lsZDtF>fiO=3D&5jEqFZA4{l&;oIX*9bF&X=3DkcDyE$ex|9XU;?B! > zLKgK@^uHSZLNEF1>n3- > )R$m&0eqm{zZExO;i0Fu8So)&JS+p9pMjqEJ8)+x~wPT*O > z|D}^Fip?>GIZDk6RgnL? > zB0akvuCxB*GwT`4EQ7G3&^!OU2Wct3D`Ev!tZx%i**Pz2o~%A7N)tG{8_7hB>4 > f|! > zK$byky=3D2^HIw%rk$yyQka#QZVgJ~s!xZu)*!gD{J8|8!WuyrGfZF-f1_RXCi@Ib*0 > z#dee-JN%6{XC4>2&2k?+M<_i&3s$`8&Dy4$@+Qn- > fDferp0V!X9B0;Fs85@B48XtM > z|Mc&^35aJhII%vg!=3DEQrC)q~$8BauWdM&_wYvaf(e`#qv)^&N2Gbl_JYkR1fHSi > Vn > zP5ZAFYbCS1g7- > `&ic_myOH<3%QA;fKjl#qL7OMB(w~ft+)F%8}+ovu5K7M>b9GuEt > zGuqBqw62~MWTgn*)*?O@7DY5Z&9htbp<^4?ffK?XlIQr^BB6roB|_GM3(A4N > Vpdll > z)Yfr=3D!KB!~$Cq97BoC1%{)4Ox)9df+Lb*^x > zmSF5Nq0N`BW?JjU^wi0YRdbnVOb8U%=3D3HzrtYj80E%`T?VJ&;&u(WzCxyzI4v{ > %xn > z$xp|a>YpJ*)?#9}#GXxInrfOMTAACKllxR89`wy0#ZrUPhuF!fgq(YGlSIji&pSj0 > zI!u?7!#B4yQCX-K&Ho)t)zs9);D<}<;B#NVX4Q1SgHVQF`d2}tUUH0`-^FvPyUB5B > zys+iAopO%aBhk2(&{2wB1wk|p&1Gi_kHmZU&C5_%H)4`2`mdEC^M70RMm5 > jhv7qJ6 > zU25Oaq &n > zWcqClI;4K71`$dm7e@!~u|BvM<^K+*vBUCPi2B4!%R7T&${I>Ecy!c~w6WvScX > 7Yw > zZRiWNDSv2G3*L~9Ds2mZuN7)<$G|lR{mf%mFoox_;cErFTJ=3DB61yeI6rjDA*- > s7b^ > zWM)A(i}fjK#p$Rr?Pkbnw3;6%h}w;xB2Sxdq(()0l}pp;w5V}jtZ=3Dp~|0Jjqc(NFm > zjPV8@p`AbYzMtKX1@uRq`>1bPMaW>#j<(V=3DYcA7J7)xa^-qHLw`x1;9`{AR- > qjhhO > zh=3DmYQDu?tBu)s@I!W(;q(uV8Ukch;MFOeEN9{UdOFI>;<9$gr3N>FDP>(t6X0)| > CS > z^GD&-H%jsm7i~c(IDGo_- > ?&4WNN0jkXn*Ntx_2ra;jH^dLoxDHI{ofaq zz6U*?uQut%OesnaU>(TZJ4odNCD7XACj>h- > %XTOuIK%jeRlC4YOaKdYsipAm+WrxJ > z9kwhVoOqH+dJ~ emtz| > zfl2M?e&hnyoQdt0CHCbj$CNlr9J8S(zj&bnQ>ocq+mf?owadeDO;w+o08Y7alV > qN6 > zKv&WtG7{jgbbjj?|E|22L?~~N+yX*%DRP69w3~B`=3Dy VH > zsze^VfExVRko!ZCuL3P7_$d|t#Nud$ zV~2Nf4nuzMdh^)v$Wi4gk-bshX_1MJm- > z5jm=3DaFjj$na3>$mvN|33HD3iFuOn0va3 > z?F+M)PuZ<~T2KEz9kW2`YRBY7;#vlci;P5jBsiwV^%pp3mm zJ6fM9TGgwJSXf38Xj3QkA)%*M4gIa?n@>_@$ed4jl{{)V>l|qmLM+}f6k48aEgic; > zO)ScHYd^W4>p1IZF- > rKAZjr4z!Gy<44AG*Ch@go#8IKW zphnJh`{fUY&)t$Vw*xTv_U&^t{4MY*opG~dZA#iloKXjGmM=3DJ*yZLW@#45Spq > ~p!H > zin}`4yt_!kwfv9*U~jyeyI@zSFd&at;40)v&e=3D%l$Qzx8+GTQT#^cxC2D*1T3iaBR > z4bgQbq9!T!B#LkE>Y`Kwm0}A*Ik8V@5*T}yS{G7@DU xAy1o > ze&p!AUY&E~p5@~1Fj36$KJ<-o(Hl-jCP;-FZM15GitHqRcHq?uj~t|Ecf`RHq)8Bq > z$9x{V=3Dna$iBTQ!__vh46!dlsRYdHcBZDgLEcb-d5Kj>373=3D4;|gla3^9 zA5#xV)i~ZNh~aBsC5erA6~9w=3DHBv!4VZF?IvagL6;XRl^Tp_XQ=3DiRhPZ$JT+k!mF^ > z7yH{Qq&Yes$$nFjc%sfYsbyhbvC*s6Pz&ZAt;Fa;JI0yX2RKl3TSB z9LY8j4mP&sA)tW#FgQ3k2N7q0dTBAF<0tTu^90^VMn##KpLzNC5^ zaH)%6TeU > zDai3Q)sDP}fd8!F4@GgYGt3O#w- > T9CSSZ^!@!jH!4x+;R`6srf@8?xsUwS9#T=3D&!# > zJ0Gu_jYYss79R?y?(n2h{T}ceksI~=3DP*&;)`lj`o{{H?bK zrrke7Kd0wy3*nh@9XksXzg$sl|5A>J > LS > zRA=3D0?q3T5MW|OBO*~~oXN&MPwIeV%tfjKQr(xPc}^^9a_9DPgJ6kQcec}<^B^ > 2@xF > zjonUY-5OnX8@qQ@9&Zo*oIE6K*Q;OAdvOpJCj=3DLg!gX<1d)>|mYO1xERdo- > 1(TU>k > zitryh*C60Y{-waZ4euj}quCjh+VzAm^NW>q- > I>Wsn^_=3D%oN0?9m<2++S;$} z?D}Sa^U&- > {Px|cMV!PSR_0=3Dp;%&Y+_rlikWu0uW0+1CR_(CAvyqgYSwMZ2^y@4wr+ > z#Nx(F<7o!&h}`$R8Qj&7Q>QzSOL+m=3DU3vg8sh$Z%$DD~JW19i)&`WCr$n|TWi > gCND > zNP=3D8bAayn^9G > f > zeU{=3DD^;JGNMrlY@SUFI$K%ACYV8gHpm^rdPzq(n7d2Z;5JzG*11GJv6z}7WnM > +HRe > zJcoENfQt4U^0l~Fiszr9b8A>+4BS)r37Bd>-`;2Q0fu5#mAE=3Ducuq6+tZcY$M5(7> > za!gJk%boF}oX4W+asn8VO#`>Ley=3D~1BQl5m!jM(CJ468jLhtz*z_Qr{>bL8m!-N|- > z;QcJ- > _4@&E)tj#aMw#`6G6D5K_WBL78G)>jwt?Zq<3A3|?TW?McocKEwu~=3D2Vq6r2 > zIClid0xe8{07)CD2)!X%#5cfHG6TY1v*zHd89-g!hJtN?o_BdRpcbbxsh0DZ+c_Y( > z&7STr&jQZEj(O1Ehdw~jS=3D)E_TYmxYyaQ&XPIq~D;{ON+8gt#l3xDr~4(%5!;S^X^ > zyiYVwy?{|`8dw1JAeiIlFI$kcll>H^Y<2)^-+LrH^gE!bHMFS45Hpwp`f@`$-o?qL > z$w7S_g;0Kz%2G>oa$=3DhlYF^y+okepKmAfw>)- > @2p`Q1mt6FY&K(r;IDDBlIE3o@ew > zPL>i=3DtYf8Mh>wmXOpc@T$ 2fn_q4 > zalAS9-3+MI=3DHRS=3DA+q&n-hoZy9(_1X@18oncpaQ{aBX)PC|l~jOdW- > r13r%jU`>=3D! > z8F9+f^5ITaJ6-`p`UaxS{qIb>fP1dU&5;`uq4h!AX*SP@RHS&|cRY#- > uaNP1Hw% zbP`?+*bWEbCXc^9{cG6TR2h8b59ROpqNf1X>Nm)M?crO!u+XmO7biWV%*!d{ > ;+cG) > zu~BpMeOdB%!2$I-hQT`rC_V~K$h*`}0W0wGp7Wn6z- > 9KqzX4ywpKgSlL&Lplu#f5K > zzRMG3aTf8=3Dp`LWq8TjQzRHsXxY5jO>qMsI2($IPU*=3D!5kyj9<+nxV+Y(xoRyhuN > Be > zzGums@UauX+eAt*aPD>XT>?Vp70n>sq#B^_kleT^b6ZsniH<0rrPtqyqS8p%qb > `1J > zbEbg9uyVKYNdE*1iJ=3D>SqJ)I$18@_bVcY=3D0kl&OwaO`6~VXc2&Nz;|Af?m0&n6 > h9# > z_6btJYAkF4$*d0rTr1XO!P95qD*jI*i&C_2t}I}|zt+^$WHU%e s > z@juu7- > 1mV;+$_+(H`GiCwDd7?XXU>G=3D{{qS(ix+bo01V z!YM(hltCeOlXusjsp>|;B4BR){r$Z!LmIz2h~ei~@$T|#hWul@wn5M8&p;- > DANPe- > zC~r|J+r4_A2f#uO*TJO&xvJZ 5(V6@7)dh5bfgA&cuHync~iJmvB~V*KtV56 > z4%Obc4bQ}m#H`ZWui1dCf- > 8Ws)*LOJHy)C3S5sTy4u*hqA#?X8V2d^rzc;iHii2qa > zb+C?$3O|3;go-#e@V-4E5^j*z3wEC{cGzFK7l-!-J-@1qmsysywzm6- > 4`oYb>K>Mz > z)@ap^9n#f?gMJ%>C*K;XYgEm~@Lo;amAKOu?sWpx4-gIH0R$|yhL0%iyEWP)- > dMFq > zm$PVSo#$pSP|9f_xhQfK_uhlrT4< zRa)xC`fCZWxDGM0t)dq$A+GY?LdB3ypFg4ezCYA #w5 > zSQe21-iJAerRfg?N^^=3Dob68%FTL6l8p_{xp%I+Q}hIf8> zo5b!C!M#V0RC);ZbbQwfd}b=3DYGse&3BXN2$_Dsn9FwNeZ h3 > z!ESlI5nz`fGJ-G(J8OveGIIsA{EsMVi#-u}vKD^mZ| zBbvguApy)(w`IyKq0}Jslc%GR+w~K9d*;Pn1+fe@+(9D B > z#q&8ji;61!#cRzU9N}~9o>=3D|_{#GaLo5TY>FoB2vii > zg&bWRK^S3uz=3D;3Y$HYMj&->(+5fj^ NcZPpT3FT+YGkGFM6L6 > zA~mqz%J*!_$<3{!WD}+zO~*sXQNMmzrXPiaD9{ved@z5s(MB- > *5bib>R|Lb3>49;t > z!|X;!M;}dlrA70+8ro=3D&c5w&9{jX#qZ8HM7Mm0}Tx28Y8|B3$nBZV8L)250iw@Z > oU > z1o^bK^- > i9ao{8e)=3Dk zK4GCix@{rnin*>PGcp23|; > zI- > 8Wx{z#5l18NSG8sz&teBT`?AdHken>MZW9rZoM+m49rENcGhd|`HC_be#>0p% > hX > z-z&;- > 5 zs2{youpi7(Utqglqv#=3Dp(%wSrH5aFxL8*=3DxSXYeBxE2wQwa2I98zXf1Ki%fivpi(* > znMw8JERe!DlrvGFg7gVTsbxYDEXVlu4W_Opq4!!ryK{A4Cs^S8b!LT`RCId|JiZaf > zqwkaBJ`^Cs>4nBiPR#JMb`d)}Gc(Fzatz)u3{@~a1>L mu > zzxUgGH6QGZt@4M=3Dg?bwK$RcpRvrxh4ox&Y4_}6RfBO;2wqt- > )j9cW`J=3D^32#Q_0v=3D > zYuS>- > !$p;4{Mn#)`Nkdb>J^8618%@GP{~f!W_ZG(B_VHb4_`%_)jI8tm5F#XU!4T3 > z&4!%WZ@0SF0&kU>_-JIt2*jBd85c_|d- > y)f=3DXrXsJH|Bd|1>`r_i9OfT)OcMXG%(U > z6;SpLK}$lNH+Qlh-cKR- > YMm$D{M@_fP|E|@zTP^g9H_)@UjcT8DWWywo>&oV-UQQh > zA@Ty(17h<>)6AuJU=3DNCdj*TTVU8)8BX4#VqXCo{x6K!98ar)5 c{ > zfIZlR!@-s6$Q;u;oDvu|R#=3De_P`$a;_^AvRlfxU=3D7@JAY=3D6Wt*_l%8e!`*%S{=3DLd= * > zR4y+03rLxkWnklU7IirKW4C_mu9mgfuSrWqZg1k&w*6Bvs|udy)Cd WpJJ > z- > `$_=3Di<_AV{MM>@G+${A$jcvrDQWr3Ra86;7gUQGMMU{HW{W%x_TA @ z*o>_2?Z8Jb6|qhL74)2q6GL1D>?>o&N=3DK7&VdxaU-UNa5cMd$3zd@- > auQ<`tjRFOs > z;LOghIQ>vUU|{x?SXlcd1d(!Bc%9n0@sPcq8zG2z!Zl3%6GAblq2A)ybxzP^A > m > zlB6bvX~!}NbHJmPnhhkH!K;~u{v;fgL6aH4b|eYs_yV45bcL)!eFA#OEPZUh0Q}AG > zO?B&mWBY{pr&J6Z7|NTelY%a9@hN$`C4WC +D$Sx > zjN(RqZ>bYB5w+-;*r#mp=3DSuRKLceNiM0#CrcR*jjK}3)b>KYA3XIykyV($aJy!m-| > z2UiCg{->;S;c%*&kXuqQX|X_oR2jXNn`DO;^<5iMe>3{6knMIF{h^=3D!3!3wktf7 zTOHkDcgdOyfsEiOu%WtEi(>#Z- > ~{Ga!Rgn}K?V}trplh8UCK9m(6W65O*TyM`pYu| > zxPk~LV415e$Gvs-tY1#0pIXI_k}Q=3DdO%)2V`KNm`XG- > KacrZ_ > zb9^@- > j}kY`MLN9F > zXaAN~^rm1C>)26f0mj^J_C)t}o_`d30ZNTB;oGO@e}U-w88FH|Vp0NA28vf|sjSi! > zyMk|iZ#tmLsEBWX|3RjlkhRatuh)2-`7N}x&Lxi(-zho|+kt-fK$0VNHs&Mk>^C1D > zaEZ2D2o0vRNGwWTLhU)^8Yd@d?Q;fNOEVxEtE2iWc3(F2T}`_M{Nb $GM4 > zROF{;hFBi- > eQvR=3DhWG+Pv9y?DrB6!Rwi~_v=3Di_Q6aAm&|LQ+n$%F_OPiLoDV??+@e > zg`wK!-l8aDa_m35OpOA{bq~|+%F-ENWnBJJ1zz=3DApQviVyhYjzx}*$lQ{ > zY)3sH>@Bi0*&kLSIb&lzz9l_=3D+22;AeVh6L^t{lzJ6+Sej=3DQr`1&}zVyU>FjC~GnY > zW#ZgFCM+a$J<&JU)ZP`wOG!6|qwNR;<=3DCCt7QifLcx#yTm@)7n T< > z2ci!iV-wM?Bu7s^Huqb- > 4XWtpk**rM!C@5P53LYW?&(um1`y@zE6rNk#CCt6d4 > z5Pnhl$<&6QOQ&>g#0vC1AHh11F8`j>>&upZX?ctw+P(F;E`iuHQ%zIzm&>w$p; > %j) > z!Vt)F9nvn8o&utKexOnR&JeiQIukEIt&=3DH)CZsbl10McsuGk=3D8pZC;?h!4Tgi&lzu > zDjuZTHwy^t7q6I)qd+|tpB|d7o!d^VfW8C?{Zr&?EMW2(V<^Q}(hgi?%- > (2Z`Wl@e > zI4a=3D4M6>%sjwY+@;+@A(M9xq0HsuIC(>xTK0?!bT^ynHl_KARPH)(c3L1S > mv > z$$7q&j?=3DMNqQuSRp`9Xw%3ena)`!GxD%67~RKad> 7j{eN?7xETIA > zEzB402g{g-&p0hhmH- > #T#5p > z#(9icBG$h;f2>n&q7qyHXM9_t#hCl?tvc5Jqoa8lUSwt)Tq<<;>6X7ekCt?lxC`XV > z0~ET_i;zV_%_?W@6PpBeuXnbkkmg)yJD!rS- > W4+WUsh43&>4RG*{QQ#GI!wphN z@F6q6)=3DfACihVPnl>ag<&go1fNu$9JG*D(V(t;Rr36Qr&z{oum2BAOsC>IZpv)O! > 8 > z(A9g&ku}f_F2k<)>9(*XlnOmF1c<&%MHADqo|BrgcRyL4X{UPz7Ns=3D_Gso2sIPC > li > zDUUUhCz8}TMNB07zA7}e%`ulb8dmyI2ag_BJpwW|U`XJkp(@- > pCM+y02CSFno2|(R > zny$~CHPnsnWv>$k=3D(lzqeglQA%#%Hu6!%b5T^4cMb|F217_5R5lkOLjBf!q D; > zw=3D{304(# Jpx~r9!pm?4kz;k|ao%zB@5Hbe< > z8VY98EUm&jD%$gApar $~Mw0 > zOZZ5=3DT>*H(5Y&5b|F$TLy`7Pk?>+6Pz|3nb+y=3DH%<=3DkcMEo?&S1vwpM- > j_UaAx zmAr6fyoIA}IvwduC#0A(&}x;x%tYD#h7EswGE(xG6HEdu- > o$d)MnXpluW?a_({zlg > zv^8#|fLrlHU*Lmmq?E2*Bcp?ji+|D;>krJ5pq~L5F@adoCT+wHmA^2=3D=3DlTOf3^N > sh > zI6COif6T#|ab*0;%0mY@UQA8o~pL`QvrJUt4l<;Q1Jp5Cc@?nX=3DsK^H> > z@#^-Ah6Ui!4W2y_h7NXiaTjT(kC5946mu5EIsuG+IQ9f zSOvIH`J&3-{Oqj2+;WmTvAVkYXZ1@;KO7S0a8<;0QQl}{u9|6;_A5vtd- > Ge_d>@=3D& > zZA!kjb;;3a9ZAx=3Dx1j$^?)eedfcEMEp@Hiq(?D)?BsPDkAIwg9ZWLIbPmr#v+2zK< > zN_8RoATaW}d{5!m3i1gyT+$O()xZN-q9!|r1Ck1l5#y|GF9)80b*`2x{R5fUvAV>n > z@}joV=3DKHxJ{NM%X7R1eKT#3zvII5_y841&;U}XW?bB9oPG*Hm^A#h- > 5 zmx@EmG9ZV(c*b8Z;c$mMs~#NLc`O39MQ?hQ&08C9KtzEpQKHE0Lwg9l=3D ^x3lQL > zOLz~OwT||427?lND^l3SJ21q9Y}2kY;~FAG9FP$|P)=3DHE;iL6XN;Wht^ > zIc;;9!VGxoj$<=3D1jQ-utcT%$}&Rpi3WSnCWi&42LuvKot0mF}`n-;m zeo2cA O%ZaQ5exOt&$Uj1Z*HD)S* > zptD`VAkBS6EGPt6fu0(&FQKEd5hMoMFbwikq#%2NpiOj$3hSqT5nT`8- > 9ip&dZ5@B > z-&*JW0-3YW2Pq- zlx68Va0&0EEg_Jnfx}zHWu>G7`XC{_MPe+D+|6Y*k9mXm78lt!v|EBfV_>3p @Lj > zOz_Ha3|0Oz433c=3Dr}_<=3DaMgLNpM!7P3TlleCAM+YXAhJLbm Q=3Deq > zJ@?4yQxX9_f5?T3hio4b410mQFl&AItWr!kt}~vq(6@x}aEXw^5)%KOl(eFhc^su! > zK&1K=3Dn08*i2k!xmxFyjCp&tkK@Mqm3K_RnVKYL<02N$e7Caq#BlNgWeAz8n`ll > y-i > z`?C11zDLD!zPgvZwnDMKE!;SzPXBQd>}az!j$_a_SOLX%J!+T8fYnvD4~aV77-?_D > zC({+U3+B_W-~48o1IOxQMvz_yLMObgGh=3D- > #ZF|U1if%4r0!Fqa4AKSYGr=3DBp4wS;j > zpOib&fjLlB^pi4^HY5YM^W3^bV B > zeFN6df- > a9W>sI&vs3 zkM^KrIxWjj5taeZn0^7>6VVeep*&B @KJ > zylHB z&VW42>)Wxuhrl92s=3DjryL3&{z1sX@KSHJ&Q- > |Gt8 z2Cwn+EhwCrhjd>|`xu6XCXTJHtnk1dIPUL&;DV*7- > Otc}jM55$Vtv405ZD8XyXxqT > z1Z6*6);)KSc0CK4dg%T3pum0vQ>5}aO#{P(tugn`fC4ZbopFsCPQeiF`o|5rZ& > z*m22MLEmLB0~&>EHpNJEr>{`_UHf@bz9YVj?nt;{B*8qXcL_Jy@95+^58e*j7v# > qf > z5L#(waJ5Q0<$;3 & > z0#ItsUj8u#y(j)y4VYwYN$gWCE`b^f)>AzAd!jD1T|V2>KW zgW63~AZz^!9{8KFcma8_90MJRsDS%J&|7|J6X1ZID#V|R5*pf14DZ70A4U}f!% > R0i > z*xK4bdu9e82m$eCNe5^m`1+0yG+p)Ai%9_%2?uRoJ08p+%l7npYOaAC{!p$C > m*DkC > zHs9kcukmSQnY-`;FsIMBQNgVP-Mq098|Xu|qO#!t^l`6t7&?ajJCqs^1g|c#TkBd6 > zpR{hR8s@e{ll100R=3DOUJ{X%j>s~WE?f7jH1^Ud|LLS!5;2@CUncWN0q>vbVLU7 > F@} > zw=3DHH~=3DP!*xj#!bM)-rh6>c${mKtTXni>QJ7c5YwV`pjx{JNI-W354EXdcSk^K;5aK > zHvx8bZ a48PghrZ+3AO}YN&bdTo > ze+0HPQeV^n=3D){b@msu@xV3hd+za&FlsxgA`2dsYy3J?e`FX%#Qaft?8zk > ce > zUC9;r{}|h?AZD?*apNq%44;oG3%FR0jh7ifFLeMtaSv^t-MgiMENG=3DijNg0)d+}c; > zB37-GyMDjRN)Ia5j->kuYeB%$r7mAv^iA8>$p_=3De5AM%Zy}ggWNO- > KB7MVoiX`~8> > z*7)IjaFf%hHbd3rW}MaKv~)d#H7ZvMEa+7%Q{~f^PXlXDs>=3D$+9z#!al8x)w0+>E > h > z-Hb4|sRD?_*C^x(zVj~uoxjbcjk&m-)PDB%_D`$)@T)xnz5%Q$VAe- > jybQSioRnO6 > zpm;JeMEQciVCPbow+)K59- > z&$!uL48A%HNCn>dxp;#*K2Mfodxi7~+XV*OEjSsX$9 > zT=3DFj=3DKZ*1n8p&@at&EGv)B~^#3@m&Xv2G~hEB3*j@(o}iMf$Jd5(bnSj4#JJfps > 19 > z%T+-UsiSJN$9ER^tN<(neg*<}@@q~?t9G}Dx|X#&<*nlwsAM- > V3W7dJ0WrB4!!+0k > z;zYZk1x)Y5=3D+Q4I`1dZe_-J!T$nHwi`7v~?2q6!GFp4Yw(%Mgr$9Wf^z&3n(g?G~h > zw*4)$A+STy91>Cev^o?Sxb^6;q5V&k=3DQWf<5VEw7aG;Q1KY > ^|^o > zc;E)~o~74@l~yyg&&)$vvi78){L`wW=3D@l(wF2Ai8JRf6?amb~~1adnQ4yGK6{szY > w > z8&- > yf@E8>lP~G(eeq=3D8mS6WA%c!?}J08^@%kHEik2dum#zvEulud`jWwD!Hn_Pl}l > zhM=3D)cOy|@+mV0(&3=3D)Wz&3}Tu892W0cGmn- > cjsU7u?E8Rr%=3DGo&yh&47sj4@fk~gw > zHbHgW)yUvdc)U*Gf#UVd5K4q{i=3DexR{YJ*2z>D&vq_mL+0J^Eqr6bB7@jA))oz; > Vn > z-7Hy@NH#Dq0Ye$cbqq?iR`tT{Rb5Gv+Sx #bB > zXM$7tum#;TFre>Vdj~M)s3Z!I=3DUe0SXR`8|GwH62$W- > 7Me#XrQ^Tzt>kfFu8R(sYw > zLTUgw_~Kc9bey8(!L5MKigRl$SrxK^#-HozIURZx??rFE2R1&lHytBUXnb8s0OoiG > z0(tf($WC5@4zV5}rfoNmN5Y7 zBZ2G0+rSa=3Dmh%RufgqeOvZnHE_88jdip1+lwbo=3DGR9dU- > `l%kXo|H)KHVD6KWh*Rz > zy}|v0KGX}QfC1NJBeuXreFw^?gfJlI@wcfb7cF63U?#eDlei%aRM!m;Hp;yepx=3D& > < > zlG`I92P`&o5QPF3^!fmpImKoyZ$raWcxfSw1lL>|>??D9()^hX)X!dkMqhTfL%^gT > zDK^9an&lf8RbTYi>wz`Gn%!tz7EKLbxmh;aQH=3Dnf+W{^JM>ncRG;+bx*I3T3ACr > 1> > ztA?Lt4IHi+*?xnx?g7u5At5mlGg7z;4@#&stoL;M2_CVx- > gL}d zOu>- > D7w<6mo^=3DnnPL0|huO7=3DZ(hxA!cwBQoDlG&99x)D)xt=3DosIyZVLPiR6BBm<8{ > zG&j0?mmIK>8&DudbNk55!a;SChXBr > Hu > zlIbQ*_5-vx6!%Dk-uQNr+Jb_aC)PZ3*uB gw(`|azE*a<_S1weMZAOo%w(L)Kl > z- > dk=3DTU*Vy1WB5sn85_XnaV<$}6CXy?DXfEPWiUwjNO&?rt8bi$d&cw6WNN_3& > $$E$ > z=3DaZ0KjuN9Z_4@JfBfw~`4R4~T-T;(py=3Dd$WnmIOPpx8)C&OFHoM&-}>8;{c3- > B{GV > zSpajTZ{QTEuIcsDJm3Qr^~?SUv|iA5G&EDlzvJc40|0qdwhkknYRshszfpeE-ii1s > z;li6VZBWk{E_YY N > zr7Z|RxZePzdOqJHasLwF{ta>g;N@8`;k=3DSN<1eSW=3D2gqJ_aKSDA@PO~Jj2JDy>fE > # > ze+59dvO9!1`KKy%xtUWc+Ka3O6*j2+V#*oEC^8Q94dXK1m*<|V>3^l2 > 7AMD > z6%mF(P$gIq8+ehK+zYAB?NnCmK~lSUHd9p~D@$#MgC9s7Y7EeMI;r amYM > zvH`de*Fjc_!0oB;es2ryN-%luo6bBsA=3D_A=3D-nmZ}%^`N$?z`4}RqF82uB)V>)}QE? > zrWiuym1^BE%d4kuv- > dD%vj@iTDeQBtR#*@Vl}}#wnx+R;HZF8S1|*3=3DE2yFi7W z%- > SI+;^Zd%rtNSOxcQz0AA>HvVOQ{`U+g3bv>?Cw@R$8@o5*nEBfVPA7jh+lHR8=3D > T > z3DUhcmx%#n=3DoUMMGkvUbHV_C8X1!ORo- > @a)b+Ml`1F ze$ay35NRLT1K%g*UcpN+klJU$8;N>w38wjVz~lci1kCL- > 69N^MI1)$EFwkLm8>;g4 > zE){hvSV?1CRSdogpA1TqPJHHqFI?S}{DAkLU#Vrjye#NG=3Dj_8g_krj#`<}TF2$5NF > zwGTg;{l7rkS{fsw1r4yrywA- > k%PLdC*dzw1? > z*7v2fQ{qIGCy437yA$y|di(tL6bIpZ{lY^>6Wt&{ZTkZ!#*c< > zK}4elE7~Y&YgV!U4qDCyR}5|`OKn(1*z$T6VxwDrI@p$*fJ3XCr|G- > H!u*oMbA84$ > zDf!{}7VK843D#Vh$}Ta>Kr!$QaJu~BaS1!a z{n|=3D6!AK_%errba^sqP~f{0y#NGSR=3Dh=3DaNdl`aZG&#rax@Dx7&qbn3w$#Su
WSp > z8`vdtPHOgO!{hWlVcp>`f6wfk_*ofE>?9~Nf&KdW7%?>x`iC4@N`!i@WrFLMCK > !+9 > zo2Zwi=3DFZ~N!#pI^CEXp!BFvxjcn7nbr7DQ^s_67;-LGvLZ->LB_tbbsj!WAr(Gsq% > zw_MRqd{G)1`B`l*-mCYdXTnk@je)$}Zi4rO5+fVtbt*OU3&evS4Vl z%|CJYqS8jaFJ zIy@ewD;`ej#S2XrJBQ87($zxdbKz*1{Db)ia=3D>zV?q^FJ6~C z2_hWx 9?bOShFo2W$of{J{gHl > zKo1v2B$WmTQ_6m1x04m3U@_UN>RT&J#5`2*Y6DHpj$NOC9QtJl23dJ^%Y$ > TzS4WWp > z%teWbTOjRq9qVnl3)ut>dUQ)f_}SV#Zn12>C}G14#a=3DC7``q9)J<}iRV#CF#isP_} > zETXWDhAcYF1^73e!!3x7#T > 9bz > zp`GCL&Yf1Hg%_9awphd@vYezbFSK;sW@15 7VlOw; > z3sB&Z#^LTT*B<=3DE8aV-1+jcGaNapb*L>5e=3DmI>Pqr!q7ke_-s9fgjkjr739K6W}g; > zP_#DKt1ZJr#fI3rWXLBlofiSpbG)M ~Q`G > zEyyglM_$F8IF$2X5R^Y=3DP7n9e@V-YX;s-2}8jJ6dx^p5^hk8Bk;vS?uYYh_l%0w`v > z8QwfoitD*@- > 5862x&Eo1?Tm~{Y1b;eUxUlvFg7K9wfNu8Zu6iVgSw?pf>Yb&E}j~x > zKygwn@YTO@*^7OBcrZwSNvGLCAjKd0dbmnrRk%tbG0<=3DQO+1yFPVW@&o} > ${B{GLO@ > z+f;_u5DzfW3*g{Ot?*lMI4};q > zw9k?sts|&t^kNY%`7U~k~@(W > z{H{Q{1- > K+v**L1UXc1|gh*;YWWyLNM1wsT5Oki?G6?N?KyB8Ho^>Joze&r!r#JmFX > zJqfgVkhU0+y+^}_&B}9G*l1TIfrImD1QDZ|Uag_~4epN0Wk@)a3*!{8wRg^id0T > YK > z-`2+H1Yr#7j9>- > Q7R$tgZ~a%1i4N>8?sfe7IxWp%G@L~oRU}l5M$i > zl(~B7Gg~f&Tn+@zXs>+0WtDf2n|H+^v9+p 8Q- > zDb>(~0BUEU& {d;Z$ > z>%q~GMLth%$)7kc<>- > ;)3FDeQOlqHXE#r+n4C=3D}QS@MyvfVTr zdlj9XC=3DX_j4OM8&Dp~zFh_Kk~W ba=3Dc > zOlZE~BW)31P7rRmVQRt7A;MW@YZ%t^m%sCj{k7MjPq?;w&7#RAn(C{3ZUTn > vLvp4* > zsyQ`W<_cqWnY?UQ3>b`9TxMll(n#r&pMMWbYzpVm=3Drj=3D@Z7*F&5SUT~mM > JXCoBHnW > zXj`JAT5yf!1BoY|+TNp_;13fdJba#>jZP?}>GE-I2(*d{Xg1uX!+z*f;`yEitU!R1 > zYbEcEJtf;pi?2*YhKK>irJB+{-W^F8)a6oD0=3D2q}>ZEv_J1((x_sC!@m~1|lxd-9f > zSOT@Xr1Bh-bbXXRT5_hOT@g{Dp(8w5W)aWZ^A7^F_u>e#G3a4EV0 2|a}K?r > zFo(mo@$6w!RCO;!k{m2{z- > _`_s8cb1@U;62vs7dN4LV`(tZy)kw0qS!Q!11UZ%C_9 > ztbEz1_46;&*;HY>ml01b1bOgokAy{FB1>uh*XlJfhmyKZ+E$$rXds@q G8 > z^#=3DOGDkg3ngdex p > z$s$VgHx3o4jk_>j=3DNP pU > zuNu1HJPbQO>i(-FAI3{0OUJ)*Suj)9njiaIzqjj|13DohJD z$O|nV5>2!S18d~_VCX5{Yso44WdWyk|E+~kvOK55+j`- > GBdrly&WQ1*?kDL@pG zy15r%4El!1g%)F+uwsybyI$e9D%%;vLB3- > JyC;SVa > zjE@6rn{H#*&~_Rrk$^~$I8-#=3DPN^_q^_t|?-i$S) > z&+V3l+}q_-YTf#5;-oME+!rd?0;de%k-B8K=3DJk=3D!(&2 z-j@}G*6l?MCT?VD&;M=3D?_K2?3^d3eRDsg}bJ$5FgMB@xQ<#- > 5>lJ2jXZ|?d~C=3Dn*V > zOe%k%3vPHn&Zy56wfiWs0vg*S7wOw>1~eAetn6;)3I%3|d+3BtSz(DPJNqRCR > 6na4 > zw-T^n&!E}(lqo|tSQ*O=3D0Bd)N*Ls*l30a+H7gRUiW$%~bo*7>~bfZvS+P$Y}i8+Li > zajQH#9E- > re@K!2m1+(vMn1G%BAl0fTGoC?L675^ > zR`crJ53$%V%1ym-z 5)vkqIFntDZMA1VMOHjx#|#gp > z<=3DjzFhYH`U9&O8-fDan`- > Y4dT`fuoj6M{EH$cYAwYCB}93Q7pvnY!%6kR9I4peQs9 > z8gXfY+U%9+QIfUrv-RMP8xj+L96f|0TKE|UW=3DsIy$!oL;)*- > U+iW$vXVhjdwb1+B< > zCv#R!fV)i{_#E&I)1j3D%PzEd+O__nwB9l@0vxf^OBj+(-AS > z2foG1W*C4?Y~g>$)7v`sHj5VWGE)4G4SN(u3s{8)M2Nr!W- > JCA@y| zRrxzUDcddaRCG#+S1yFOToog>HfknfH$ n$&cdT0?4AaJ{y8Haf%6 > ztBCCGSvjQ__Zs_qc0ioj)*r>>nNWnFgLcgV> > z^pFUzNJBKo!`J$U-;#A>St>e4yR}- > zs(xvgxPQWiN$sNZR +{ > zqPsNRQ2{%PP-S!ukCq^Jh_wt_D|VG LbUdRv*H4Cj>iO=3Db6AU_Wkt_g > zN=3DW=3D4;kE1{Z8SE4{eMx%0k|j4mIYN3c4n#E9cuyL$*+>`c4xDUU&kujvGsP><}& > (L > ziB<$da^~Lvx*5xgy++?@K|G?LB8(fubjF6-dFS5is`M~bDz>$vvzPgi#c=3Dy}m?56~ > zeqdWidb5&VSRMO9OZ`HwGV_ZI`dR){noB9_sDnUTS@3hqC3b{ibKOT)gLqCA > IuWDE > z1y};% WSRlcK > zA!c58g1AC<{{(N&%*e-fRXOWZC8~~T|1k8kB5Cf0<>aE- > @Y*KD|29REbj1RxL=3Dh8r > z>+fB0RQ?xV8~i=3DY`+comNUz$q`%F8Q`XO=3D5kHob}M$0h8r&iu16XArAR5vcz7L > M^W > z_l0MhYu4d}a8@DFklGq(zEifZ`n>LW6E7ZR4w2#&6?rZcOGgeVQ4yLmo=3DJ>L7? > _YY > zh;(3>zy4DXEJ+bjsn`Iv)wK4q?j(>j$8ur(Gk*YreT8Wq!()EpO$qelIY&yM=3D$Wf( > zVw!caiJTMU912uWQ52pw=3D2PzaLhb++yL8sBFt#1*4(V%FAdSlIV7|bP(8F*@W > u$Jn > zRaIs^yAnMwrNj2sVUI?;8^IcDZSyk>kATGymF&x%h}BQRzm^bYk+gcYN6OW`j > Uyi# > z9`L}usF?50NtoIsJ-ILb1WoIBu?MEj06ZlmLL@N!i!VM*^e=3D1- > 7wk8y&7D+XUbGqc > zq$zAUBQKS3cDK+FZv*c#3nvye%cSD9ImBX}x=3D%Lm7nD_SC)Cssol^$;07fal z{8HuOa0hu?1Ptjp0o#fQAX3nu@V3omX!Vz5-S;{5gFR- > `ynB}GDY7)z8WdiaOz1?$ > z#Wvg$62Fmk+c8`dhkr*mwTTOM&$kdO_l5LV+~0@N%O1?cBH^E@32>Vxo2 > w_fKH^bO > zD)#2Vai)r_B11WH<9rKY6?f}OVeqDzYTtQ$$!fu$^tZ4IEf&7AUagbH{B5mH{!C> > r > z?!GKDBpbj4hmO|2pJDQE)JgFuQy{V$^bXuRLYQNZ*Q2TAuk(wQD`>ijN)_|{_Y > b}g > zB}RUduv(g5LjT_L=3D&1!ZB0Z4!n)mBtIZnG)M-{XinIi7VVr+Ml&Uk80i^?uHs^eFi > zXl`9?- > $MW^^?Kvyr=3DpvyYl@UlFn|V!pF4MKtS!xA$Gp~LHOW&Pvao29@(BH > zQPs%IT;9S({?mP*=3DQnc|D5J-|w6F?FKZTjOGJzXLIUY*Ru2cca{Dx^$QP3^v9nf=3Dc > zSN1ZZVIUK*PSDoaPh^-w10GaNc#DX+5E^4%Hq4- > F^%90(P& zBw90n4C4q- > z(vLpGx|Y+-L{mXx>ADwaHTV3J@HNAALJO5#Jh+9E7(=3DC&1#5R*tv- > 0T#hDV!d^^pp > zD=3DKI;vE6prEu_&FkL3b5ls@u38`P}>koC6hw@9)bj{X2!Z=3Dzk+ejoRZ_UQ#P?i}X{ > zrb9a8cYS#_+gKih=3DL=3D`I&pg_3Ph5pKTnl$j{+o$Bqbv=3Drlzv8bfz4To&~4YEW{6I; > zM&VTo1yNiOmjRj!72NfL3)}*As(LJyU(NE$Bvq*{h5oBrhFoP?fMbNwOire=3D!JJu > G > zsH19Sgn2-N`pPG;3Da- > R)wUYvP#%R8D{%W4M3=3Dp@E&YpC5Y<^3Ym > ziNry8BJO!6Gs{Spn<~p(CkfRkgVTSPn1xG`u-VH<=3D3VIqG-dJqz7%-wXl_NyC`0qB > zZM%WkQ>90*E%1Hy2R)%lpy=3DdqfBUWbktFoL{8FnV>VG0X?2Xb wIc*7PU)!KE > z_#*araYa0U+l a?2dg6PVM6GVX~3`oj%%M; > z6^{+szPS#p_f>%2S%je > zqp#XEZjXudd#julr>lbGEvjjHhMj>&zMCon58tRQVv_JA)Ve0^^yI!9er^r8x`y}i > z0^|2WTfYsA_>Kk7xj;Wkz0hg0;^oZ~QmgRs8^9CRzj?m~FgA%a9fCV5_)Pi!qk`> > g > zZ#4%WL}dk{_-%f~DgU9Xd0caLHuWI32Vc-|bSg^HkL<46Rq2oS10%T- > 2DeQ#fI0ra > zt6wR&OrE`!68Hh~=3DHT&7a0EF%XCEs6D6<|DPv=3DfuY9Z|Wg>Rc{ 2ISK > z7k~rD4m&7xFAX&{$U- > )37r*j>AyS^AcF>rlN#P53g?MWu@;0ghXJwHmyzHPAm&$g3 > z`1$18`e42z6cX`{J{4$jI2uf2_gZ@V0E@y;uQ>$(sL|;uC&2K26M`_oq%vabh8J1n > z1aL-VJQ)6dO{5HCl^)iq1qlQ*@lX46QF;- > d#*zW#Tye9hV)P&G*REZ2he+xK461iS > zOe*6~6)mek=3DE;_8uuk7U&#*H5vR@QX3Wyx5GIfi5!bd`c7+- > @izT8*fdO5Wh=3D > zGzN?ht%-;PqozRov8jp4bT1fme{aSh!v>UV9a5L-0o`o~aP?IA9Z!O-U`a;Yy~M(7 > z>2dg0OmlI4rM$fS13oG`9;7}2lHqp%>~TQIr}hx!JPQDk=3D@6}0- > *)&Q!XszxM?Zkc > zJxsx>FboFMSO_UsBIRuzb;YY9gvg>-Jfv0&0L`@`(iY)p>yfDW_@HcbVsotY!jO4s > z08I2ZtgBns)*2uzd{t{K;P3BM2(E{<0<@&7@olr15`+pC8sOjJs@Mh?*Adfg) ^ > z!JW4a_lmE_m7}#-^(IB?0iFB$A^6h*+- > z(S6WLx3G#Z3Y%Zw^ZekvMu6ajrh9YmQ9 > z7-54~(UFH?f-J_YMgRjt?>Pj6Z1R+w_BE8};H@ae@Ul+`r*S~jVX*?4uNlei{Pp8V > zmVbN(^|1#(h+-lD+oK- > PFmiCbaNNAfV|D<)!3S_H4j~Ez5MxzGt0E5qs?(|Ia|;Wb > zObewKL- > P(n_4)MK&oN(E2CtnHoZ{Uf84!3wJa{usu+FDdE$udUN03e zqH`^B_5D901qr8fvd1Xu`fR@xM7!oc`cNYw(TyzV2pd>O0j%K6kaY=3D*hR`=3D{GJm > E} > zSQHPlHH!s6gms9bXVF>}0F5dT%}`rqL(g7j5drqNfWy)N<+woi?CW%Xek$~q?FS > Ar > zP%2zztgpfuZW98*d8Or41WZzvm~? M#TotF > zf2^1WUw|=3DjGZ{M_KC6YOEM6I@- > R;axyygv)z%w>J;QrTqM|G|{NPAfzth@dZ>-!8~ > zv{li;2o8-iHTEHffIpQ^+w=3D}y$xOQ%*>@9D_c|rUAQ!t8`3C_or0$Qvu4IstSO-Gw > z_KKgb4QMQlOqh#&87!3f)y$gu(`BZ1MAUU!1UH=3DHg_{EVMJ}b&A&tdc(1{tupJ > vh0 > z2yd+qIez;p=3D{X=3Do?0- > ^Q5e1ZDqF$EWLwk;Wafrm&{*U#O+U?v8JB+Y2(2i;_h6F9F > zPt2-p)e8c<$mYLVhT~bJDZ?-u 7AsygbO%g| zD`%m<5pJYWmrlB?SKNYqR@RXWMU&gv+h5yxq;k#HJU18?sR4ZR>)UY+(ac7 > mcGpsI > zXo&HFMdqhPGxn)K%IBDgft4Rh5@7S1TVo#Sm+}~c0t22w;lZD@Ryh7W;8t@z > eLf}n > z{4z3E{9b9XBpKv@UhQ5mpj2{3sjNuZwE!H+HI1X5h4xeR^1Be9`&XydnwnJE_V > 58p > z&@uEewo6m=3D{lX9bV~R}@7XS!czLY$)p%e(sq~EUR;IeZFJ=3D6|?4AYy1QVkb!EE; > _G > zT@lwGAo3iS MFD?P*U@3j?4kV=3D+6P&6r#j+wuE5{zCuL| > zj7S)yR*>QrPJVHH%rC6I!f?6=3D7zcn$WPnoUjkyKt>OFePyty};R9@)aO1RXSfV+Y7 > z*L9AUT{ijtMS0dcKOCKHpr- > +xSzy>GMi8eh^9$`m0MO)I^4#)6|9fq0Y<#+xpDRp+ > zM}!>G-HW_0p+C0|#wR#aRBk!spJCi- > eFOmG^@5nqfLK@zsHWvFIpCjl0MVjSQ! zpbPMU-L7qvJMsXT>7cml^}7Z!%s5@IUxC+CukMlD0G?v&MG2S)m%r- > sKrwUsCLRyE > z#47)JIo{@Gc$&*SQq7M+>)_zv#t0(fLNZw=3Dj<$bgGfz6vU0_QXr0m@iz8VH7H1 > shL > zIR4(`^PQ8D?+rA7H_YO#z&)lcnZ{)&bPUlp$ONhc- > PRL>58O!#8GQp{Cl!yQ=3D7Ue@ > zz}|n}(`toMfrbW9KwZjq8o**Dlk-;+gP+U)KKu5a-Xj6S$`8%)2B=3DC`%#X^UsJk;r > za0em#XMjE$H}AJXLcx~`xsuP8r~t~wzdDjkK}NteCIJ8dKfMq)4coEKZ3U@*i;qp` > zPB%^}<&X}`R8 q > zo3WCw1P5}HwMm*ukM@ B$kndG=3DY6ir}mBr@$2Y+D1hg2`HSFo?UV4 > zWpi&inW)}pTDjzcWI2#9hs__rze4CYs- > U^p^V*Gvjo14>%6c8@4&Vnrw*Vh73t+H6 > z7L~CGkPPVw__7_!r(O9hx{|;1vb=3DZaW&x?!74dY2I- > @dCgu?*w68w7U@T2W0@Pm_Y > zo{)`1QrI zD7p1*-zR$f%3svc5cpCsTy8A zy(%Iv1B}78tU z{t^|Spx)=3D5v|Ro*L$|qtIsxiScHK-ah(PHB2!PM{{f9CL8Q8qLxKR&v<*sT%{<50P > z1nf4JzkGvgr`ceSUg-Y`cF~5%)%J9lT>k3ffBAN4mnfgI#6r1{rkfnH > z3w%^07 ^vX|KAVC&{2*hQ}^dF=3DJE#ui~%UM6t$g7Mc > z4&Iub*Txa g4XT > zZ=3D5G9{;#<+|A(@D-*`w!h#^~%Z7eCfk(8yWEHh+@2- > &x>7Mi3KPo##3Vi5A!D~zVd > zkX<7pMv*03WX~3&@;UFG?;r8?gP)AoyzcwD&ig#C`#6rb#JVJ@@SZXqE=3DkiHJ) > g}U > zaCf&=3DE9^#yykPf>WD5}t2)2xoMPL#bz+*&6H+?C0Y2J+t?v&`3)G3i Kg > zGL`gU5E4`hnCF03rvo2knVHf3_ju{M{ AD^;!gx| > z$J~t&UI|{AvEA-al_eE2(%@PCK;4|OK5}iecCLodATIeZJLQicVz5Mt@)_)$Jam3R > zfU=3DV!Oqro%ZxywKyb9`DgL<+Beu-V- > O2C z+=3DNgcKj_}SY&equQvt3G%*eGwP*lfhW(r=3DDLKraEL`}p|gP5_mDN<`YH^(h8gz- > DH > z(BhgoRx6F)gH&%z1gjO{)|9zOm%3jT{YO*7RnKy->D4Sb%Y2fkipxK`3NB{Zs52m? > zJAbA$-Qwf)_D;q{KtfnuNw2wSxdJ zdLyyo|2TI~EKjc^>a*_m%Ol- > KEmeMx`wo+msu_i=3D!gCUc5aAFo > zR9;S2T$3VK@oRDw+rKWaVw#h1yG0o- > UlMiBIpwFoxSL(w@3J7A9556*;P>naq(Up` > z_KUHlp}~|F^iFZ`BqAC3k4yLzd;v&SH!r6$XE-kp8;RCbBEh(6 zzRjDd3h~J1Hg$lE6JsA5sySOo!$jtgrqiK?>%)^(o5f`)YKZS$1gNhfX zCcv6ub#!_#%}YQB`RrePG;=3DqsS1eB6Ei5k > 7 > zCM$e7c3X>YfQ7+8MCdW<^Ao0?i%q)fW+o;VF1H=3D7sDyU(rxf}X+YO#K7xm37i > ?0^U > zk(ToVyjirpyQL$jO>3zd1nZII?H5JbQL;L6MpXsy_U1xSGtk-E?}44V&fVSJuUy6C > zVZ0QL^9EL}Q+rmi0+hKCO-*YX5LjRjE!S(o>%PV^C?8n8LD5yyz4Rs`Y{K6Fv~xq$ > zqyU%D$rVf!QSlm!sQf^UwDbAVGkgNkl^g;@#_W_4%H4i!*h)E08g+6)dcYHm`` > jz` > zq6J3zB74#{#24u8?Sq?$73g2Iqy&8sQNw@OWqAQ{xXq;h(V9rMtoD^%stpGqr > T%2I > zY`&F}GibMX+2CswflU46a`GXXqz}}}%s z?R1wSI_kK*>vW2pH$b zKjk3A;}LDQ<6MRmzV<*!kNE~tpn%yeuoRb;a@QFL}YLKkHmIKfy5eh*!t > sx > zj@MvAtCYI0^B_jOsGmznnqp$A^7{t7{ik=3D z5z?0FNFoEgR!az3eGPt5>Cjf7#ex4L7%Kbn8PyOncovR<*8(1jS`KwPVjV8snWiD > & > zU8IMHJ@H|a2CBLea6j!Fzo;6tfrQdE33T@lq*@2vHC@wJh)s^X@ > *1fT > zH*CIJkMPp!c5uW@p`YSD@)2jJCjZ8JLHB_W9Wc|KckjxjjJr- > G6WS1r9|13yh@Y{v > zGn6fLmEWcV1l98v4@VngXn*G--g=3D_&+3&(z {$zk^x|sZJlO+j > zD@^qM5wtrWID6Oyl8t{E- > +S}qWX$IM(w>_!tIsRG%pfTY&ct`QT#aAqO)g#>xLE}S > zE- > yoAm+U0lf#AKO(M$RN3hkxX*gM;WgM_pS!W3G?hk8b!Kf3~Ub|%7|eJPl9`UB > K8 > zQ#_- > WdUbA~qEs;3RKP0$ES6?$E%oALe$w!<#oe^G)Wlb#MWK}|jXb)QuJK2sHLzP7 > zD@eL0R<6h`tN`(_$jjTl34gsUXX2=3Dpw=3DCYhg0msJQ7Z!i4K62nPkR?Ue#9r6om*T > H > zFf3NfIr!U%a+tukwTmg6*9{xQTm{(- > 5!57 zsI IOHVAv;9JYqgFDnPH^i`YXUEuqCyH#w@_{b=3D@_pT(bd=3D9aE6Dk*l > z*kS%5D>8oMvkKYD5V+*hJHRKJl>h!@SXt34BP@?*_Qe%G?Cgnfi3hypN9cCtg > vZ`h > zB!r5_<;BD32UQBjJIcBC&*szKlB2M;cl2@Qv*0AdG6- > q7*)zY+6;7LJvM>HeW?MMr > zic`&sKjKEIehBAU^B~J!?-!Akq8iGqye|p4J#A{KkA#3GDvx+CX8J8+8~}1`G){j% > zZ2)N?_Ap!!4LenMpLLzo*5ubP$I!|^^o_)l7^Wqy0u- > _fVYHP%QJS6G+)Vr9%12>J > z>iZ5+E#ve(TOgMGZENWJNYQpIq*&gjWdC|j7S5OYkZGUjRPahLtRx>DpT0twM > k8(V > zvHhwDNor@^qXCUj%)_zBQ#}&2vOI5*yhv!}su9VlE-IFbN%)- > ?sMK$L?>Ng)`3v`R > z_I4IAJG=3Dx>g;7LAsk_X_>1eGB0P!=3DnQ4T!m9pCWh@RDmisz22HCZ(^_6D0mTl > NJlM > zebUH2=3D^uJr3jZw?J0~IVrp9~UX-egotj~aByAi-f1#Bw(VP z_X-ewXo#!y#RDQqNTtC6obA98<{l zk6=3DztQx5L*MBM3oVvZ%p(()Of?ss!m9hPR!NC;du8$ohMdRTc>4- > kyiVyjuy47$@| > z_!X?Yt^*aNMy~~m!*B9#rQ8fqUUn|Dhz0}$)l^q0ED1l4Q@!^7n&HHa9%>nkq > 6 znU_F{yaE)mGFu|(^o9_jvG-3^TJ$#@)tCP&=3Da9dak+@B b=3D1; z0ASYzrLCvn`bnP`Vb96*RB+uc>SJlsou{TNg*u=3Dv>m|MH|>Y- > <@ > zqnXu^?bfaD7wXC2ujaC=3D@Si`i{p)gtHv_#*5fUTn>Hhqhgb-%NnQFS!>!e+)H z38SuzeKUTD9lXM+o;hN&fhC=3D_Jk$=3D3%pgLgK|9T )L > zn)qosOwQ`WUu0h5 @%MC4@d0h&s zVmxCkZ(Ob}y6??ZZ=3D@=3D+_>x0vID92e@XmVMr$xUR4{bUU8YX?5+V- > _M01I|m8wTMI > zT)8#Jo^kh2hOzXFqnE3>JmJblv8m73$_Y5XxOudQorhXT@CqAJF9n@pyeX5Ld > (#T` > zaU6321(_tiB|XrMxY6GJM%mmruYY+_|8&cIz7fc}hXWDK- > OFjWJG?jVvSoZ_a6jbw > zChVCp3MU>l%_qdCKl|=3D0PRCZtc}Ny&YXMx1L&g}|vej_av8y?zE!?7}IH@IjaIaq > Q > z%33;(x B8hJF!+PDU)2Ng~^@q8be+U > zf`~E5@F>wM%Oh-1{iEi!crI+p5r=3D9$gJ)(RTYsf0J4Ygl)^B5-M$fLt*W!CvlExyk > zA?|XIa)1h#ieK(S>CeZJ#3!37O318rM2joStx}}#8=3D&ro zp)sYYlcVO5wo(O8Akg4cJIm7zyM%rusiq1F#IUoLPZmqWJq$i0IYl{k@no5(*@L > cp > zKSVWp#RY$Lhkns{H6- > Y$pHTW51f8ryaqKs^Fo{3!q$IHG?q9U`4(p{xHu7_yW0xd} > zXedmc{`8kfc72VEq+1+=3DCq|Sh!R zcJS1QW5n+eWRUpomR#2Yew$F+FavAWf;M>EFn*- > i8r$$4 > zZOqr7#;sW- > rls;kbWa}BPPIUTEL+%GaWHTPr#OU?ZTOlzR^do#DU#dGWUiy_;4Fzt > zap=3D%geq*b`Jpbp^{nuB!7H+a|2g1-g9-M^dob-K5PWDTzJUeq*If}X<4`_2c(`X*0 > z6;L!+h$=3DhX6x?t- > Sw&oO`JQ~q=3DD7w(4sY%VHWPKO{5@inh7_$yR%7@A2IMY z*oJN4X0i$qFv2ZX6a_ShI=3DMmJ{RLh%f0^vAe7xv7cwvZ4hnTs+QL zsy > z&r3>eq@*BKMfN&W*>mt#- > zt3%;~$YP+%+f4(!r89$xw&BDSdKI0Yz40Krgn2d4t+{ > zl%s@OOs5Z@rrvt!s?1^;+%Jb+bFGuv?XlL2Y98&2Brg@d=3D5!K^wB59ar{_3j s > ztH^!sfAhHeID|CCynWom8r0j>JF(g?uapPBlP@4@ycH@;lr%|*LIq+|*c|zS&U; > ?2 > zLnm=3DD3yZC((aScvn$Wid0V(2p9=3DmMb^^gA05Zd&Ga;N>pGAI4caFQ=3DayWUcv > *TAdi > zH1KZ!u{}9IP_h2$BLX6XljX^pK3;Zt+j42b9qXVMxGeIbd1X6xz)6^f7I*VVICVLS > zgMq{Gq@-5Y2}}gUiD)b{=3DLF{+EEE{L=3DXHq4z+;Z~8@fX1d5|j{Asr8ha!SX2>bi)I > zbW}=3Dbui~wHNFiLZXvCu5H=3D v > z+&D5p>g{+oOI2XlhAjgAPpXNfOK_sm5aj_Xa;zZTU!%q&C)riN!EK7!0x;r;ma{kK > z6z3Hn2ZybTE$Ba!STMp221W@^RZZpG)3Ll%CDe7G |Tz > z0~t{qJL0!I(@HQ)l7>|nC4b_cGRj8%YE3NLQUN- > WUnk+a@~B7zHP#Z}O$jL{+XbfG > zzUm=3D^61A`h{;v8%^9P2Pbns)^k49|~XURa#533KR^- > ;pwarlq8+@E8O5*D1MFF1&w > zR1)*L>alz8aW%)}m*VPcrjBjT)}yQK*ju{eV)5t$?1d6 zKT+*8miySEG5}u>ac7rqL@2gZbnM@A9rM8)7Sg;co ^H > z&#crpnn64+2ZGERDu1w`$nM8+nDj;eCseP9q;PZC`cU`&Km7XV*7jEIHrrQ X2 > RBat2OV|2> >=20 > literal 0 > HcmV?d00001 >=20 > diff --git a/BaseTools/Source/Python/FMMT/PI/Common.py > b/BaseTools/Source/Python/FMMT/PI/Common.py > new file mode 100644 > index 000000000000..efab874ee08b > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/PI/Common.py > @@ -0,0 +1,81 @@ > +## @file > +# This file is used to define the common C struct and functions. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from ctypes import * > +import uuid > + > +# ZeroGuid =3D uuid.UUID('{00000000-0000-0000-0000-000000000000}') > +# EFI_FIRMWARE_FILE_SYSTEM2_GUID =3D uuid.UUID('{8C8CE578-8A3D-4f1c- > 9935-896185C32DD3}') > +# EFI_FIRMWARE_FILE_SYSTEM3_GUID =3D uuid.UUID('{5473C07A-3DCB- > 4dca-BD6F-1E9689E7349A}') > +# EFI_FFS_VOLUME_TOP_FILE_GUID =3D uuid.UUID('{1BA0062E-C779-4582- > 8566-336AE8F78F09}') > + > +EFI_FIRMWARE_FILE_SYSTEM2_GUID =3D uuid.UUID("8c8ce578-8a3d-4f1c- > 9935-896185c32dd3") > +EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE =3D > b'x\xe5\x8c\x8c=3D\x8a\x1cO\x995\x89a\x85\xc3-\xd3' > +# EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE =3D > EFI_FIRMWARE_FILE_SYSTEM2_GUID.bytes > +EFI_FIRMWARE_FILE_SYSTEM3_GUID =3D uuid.UUID("5473C07A-3DCB-4dca- > BD6F-1E9689E7349A") > +# EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE =3D > b'x\xe5\x8c\x8c=3D\x8a\x1cO\x995\x89a\x85\xc3-\xd3' > +EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE =3D > b'z\xc0sT\xcb=3D\xcaM\xbdo\x1e\x96\x89\xe74\x9a' > +EFI_SYSTEM_NVDATA_FV_GUID =3D uuid.UUID("fff12b8d-7696-4c8b-a985- > 2747075b4f50") > +EFI_SYSTEM_NVDATA_FV_GUID_BYTE =3D > b"\x8d+\xf1\xff\x96v\x8bL\xa9\x85'G\x07[OP" > +EFI_FFS_VOLUME_TOP_FILE_GUID =3D uuid.UUID("1ba0062e-c779-4582-8566- > 336ae8f78f09") > +EFI_FFS_VOLUME_TOP_FILE_GUID_BYTE =3D > b'.\x06\xa0\x1by\xc7\x82E\x85f3j\xe8\xf7\x8f\t' > +ZEROVECTOR_BYTE =3D > b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' > +PADVECTOR =3D uuid.UUID("ffffffff-ffff-ffff-ffff-ffffffffffff") > +FVH_SIGNATURE =3D b'_FVH' > + > +class GUID(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Guid1', c_uint32), > + ('Guid2', c_uint16), > + ('Guid3', c_uint16), > + ('Guid4', ARRAY(c_uint8, 8)), > + ] > + > + def from_list(self, listformat: list) -> None: > + self.Guid1 =3D listformat[0] > + self.Guid2 =3D listformat[1] > + self.Guid3 =3D listformat[2] > + for i in range(8): > + self.Guid4[i] =3D listformat[i+3] > + > + def __cmp__(self, otherguid) -> bool: > + if not isinstance(otherguid, GUID): > + return 'Input is not the GUID instance!' > + rt =3D False > + if self.Guid1 =3D=3D otherguid.Guid1 and self.Guid2 =3D=3D other= guid.Guid2 and > self.Guid3 =3D=3D otherguid.Guid3: > + rt =3D True > + for i in range(8): > + rt =3D rt & (self.Guid4[i] =3D=3D otherguid.Guid4[i]) > + return rt > + > +def ModifyGuidFormat(target_guid: str) -> GUID: > + target_guid =3D target_guid.replace('-', '') > + target_list =3D [] > + start =3D [0,8,12,16,18,20,22,24,26,28,30] > + end =3D [8,12,16,18,20,22,24,26,28,30,32] > + num =3D len(start) > + for pos in range(num): > + new_value =3D int(target_guid[start[pos]:end[pos]], 16) > + target_list.append(new_value) > + new_format =3D GUID() > + new_format.from_list(target_list) > + return new_format > + > + > +# Get data from ctypes to bytes. > +def struct2stream(s) -> bytes: > + length =3D sizeof(s) > + p =3D cast(pointer(s), POINTER(c_char * length)) > + return p.contents.raw > + > + > + > +def GetPadSize(Size: int, alignment: int) -> int: > + if Size % alignment =3D=3D 0: > + return 0 > + Pad_Size =3D alignment - Size % alignment > + return Pad_Size > diff --git a/BaseTools/Source/Python/FMMT/PI/FfsFileHeader.py > b/BaseTools/Source/Python/FMMT/PI/FfsFileHeader.py > new file mode 100644 > index 000000000000..33c49ffb50bc > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/PI/FfsFileHeader.py > @@ -0,0 +1,66 @@ > +## @file > +# This file is used to define the Ffs Header C Struct. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from struct import * > +from ctypes import * > +from PI.Common import * > + > +EFI_FFS_FILE_HEADER_LEN =3D 24 > +EFI_FFS_FILE_HEADER2_LEN =3D 32 > + > +class CHECK_SUM(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Header', c_uint8), > + ('File', c_uint8), > + ] > + > +class EFI_FFS_INTEGRITY_CHECK(Union): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Checksum', CHECK_SUM), > + ('Checksum16', c_uint16), > + ] > + > + > +class EFI_FFS_FILE_HEADER(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Name', GUID), > + ('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK), > + ('Type', c_uint8), > + ('Attributes', c_uint8), > + ('Size', ARRAY(c_uint8, 3)), > + ('State', c_uint8), > + ] > + > + @property > + def FFS_FILE_SIZE(self) -> int: > + return self.Size[0] | self.Size[1] << 8 | self.Size[2] << 16 > + > + @property > + def HeaderLength(self) -> int: > + return 24 > + > +class EFI_FFS_FILE_HEADER2(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Name', GUID), > + ('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK), > + ('Type', c_uint8), > + ('Attributes', c_uint8), > + ('Size', ARRAY(c_uint8, 3)), > + ('State', c_uint8), > + ('ExtendedSize', c_uint64), > + ] > + > + @property > + def FFS_FILE_SIZE(self) -> int: > + return self.ExtendedSize > + > + @property > + def HeaderLength(self) -> int: > + return 32 > diff --git a/BaseTools/Source/Python/FMMT/PI/FvHeader.py > b/BaseTools/Source/Python/FMMT/PI/FvHeader.py > new file mode 100644 > index 000000000000..aae2feae844a > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/PI/FvHeader.py > @@ -0,0 +1,112 @@ > +## @file > +# This file is used to define the FV Header C Struct. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from ast import Str > +from struct import * > +from ctypes import * > +from PI.Common import * > + > +class EFI_FV_BLOCK_MAP_ENTRY(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('NumBlocks', c_uint32), > + ('Length', c_uint32), > + ] > + > + > +class EFI_FIRMWARE_VOLUME_HEADER(Structure): > + _fields_ =3D [ > + ('ZeroVector', ARRAY(c_uint8, 16)), > + ('FileSystemGuid', GUID), > + ('FvLength', c_uint64), > + ('Signature', c_uint32), > + ('Attributes', c_uint32), > + ('HeaderLength', c_uint16), > + ('Checksum', c_uint16), > + ('ExtHeaderOffset', c_uint16), > + ('Reserved', c_uint8), > + ('Revision', c_uint8), > + ('BlockMap', ARRAY(EFI_FV_BLOCK_MAP_ENTRY, 1)), > + ] > + > +def Refine_FV_Header(nums): > + class EFI_FIRMWARE_VOLUME_HEADER(Structure): > + _fields_ =3D [ > + ('ZeroVector', ARRAY(c_uint8, 16)), > + ('FileSystemGuid', GUID), > + ('FvLength', c_uint64), > + ('Signature', c_uint32), > + ('Attributes', c_uint32), > + ('HeaderLength', c_uint16), > + ('Checksum', c_uint16), > + ('ExtHeaderOffset', c_uint16), > + ('Reserved', c_uint8), > + ('Revision', c_uint8), > + ('BlockMap', ARRAY(EFI_FV_BLOCK_MAP_ENTRY, nums)= ), > + ] > + return EFI_FIRMWARE_VOLUME_HEADER > + > +class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure): > + _fields_ =3D [ > + ('FvName', GUID), > + ('ExtHeaderSize', c_uint32) > + ] > + > +class EFI_FIRMWARE_VOLUME_EXT_ENTRY(Structure): > + _fields_ =3D [ > + ('ExtEntrySize', c_uint16), > + ('ExtEntryType', c_uint16) > + ] > + > +class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE_0(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('TypeMask', c_uint32) > + ] > + > +class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('TypeMask', c_uint32), > + ('Types', ARRAY(GUID, 1)) > + ] > + > +def Refine_FV_EXT_ENTRY_OEM_TYPE_Header(nums: int) -> > EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE: > + class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('TypeMask', c_uint32), > + ('Types', ARRAY(GUID, nums)) > + ] > + return EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure) > + > +class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE_0(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('FormatType', GUID) > + ] > + > +class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('FormatType', GUID), > + ('Data', ARRAY(c_uint8, 1)) > + ] > + > +def Refine_FV_EXT_ENTRY_GUID_TYPE_Header(nums: int) -> > EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE: > + class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('FormatType', GUID), > + ('Data', ARRAY(c_uint8, nums)) > + ] > + return EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure) > + > +class EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE(Structure): > + _fields_ =3D [ > + ('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY), > + ('UsedSize', c_uint32) > + ] > diff --git a/BaseTools/Source/Python/FMMT/PI/SectionHeader.py > b/BaseTools/Source/Python/FMMT/PI/SectionHeader.py > new file mode 100644 > index 000000000000..c2cc8e0172fb > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/PI/SectionHeader.py > @@ -0,0 +1,110 @@ > +## @file > +# This file is used to define the Section Header C Struct. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from struct import * > +from ctypes import * > +from PI.Common import * > + > +EFI_COMMON_SECTION_HEADER_LEN =3D 4 > +EFI_COMMON_SECTION_HEADER2_LEN =3D 8 > + > +class EFI_COMMON_SECTION_HEADER(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Size', ARRAY(c_uint8, 3)), > + ('Type', c_uint8), > + ] > + > + @property > + def SECTION_SIZE(self) -> int: > + return self.Size[0] | self.Size[1] << 8 | self.Size[2] << 16 > + > + def Common_Header_Size(self) -> int: > + return 4 > + > +class EFI_COMMON_SECTION_HEADER2(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('Size', ARRAY(c_uint8, 3)), > + ('Type', c_uint8), > + ('ExtendedSize', c_uint32), > + ] > + > + @property > + def SECTION_SIZE(self) -> int: > + return self.ExtendedSize > + > + def Common_Header_Size(self) -> int: > + return 8 > + > +class EFI_COMPRESSION_SECTION(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('UncompressedLength', c_uint32), > + ('CompressionType', c_uint8), > + ] > + > + def ExtHeaderSize(self) -> int: > + return 5 > + > +class EFI_FREEFORM_SUBTYPE_GUID_SECTION(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('SubTypeGuid', GUID), > + ] > + > + def ExtHeaderSize(self) -> int: > + return 16 > + > +class EFI_GUID_DEFINED_SECTION(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('SectionDefinitionGuid', GUID), > + ('DataOffset', c_uint16), > + ('Attributes', c_uint16), > + ] > + > + def ExtHeaderSize(self) -> int: > + return 20 > + > +def Get_USER_INTERFACE_Header(nums: int): > + class EFI_SECTION_USER_INTERFACE(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('FileNameString', ARRAY(c_uint16, nums)), > + ] > + > + def ExtHeaderSize(self) -> int: > + return 2 * nums > + > + def GetUiString(self) -> str: > + UiString =3D '' > + for i in range(nums): > + if self.FileNameString[i]: > + UiString +=3D chr(self.FileNameString[i]) > + return UiString > + > + return EFI_SECTION_USER_INTERFACE > + > +def Get_VERSION_Header(nums: int): > + class EFI_SECTION_VERSION(Structure): > + _pack_ =3D 1 > + _fields_ =3D [ > + ('BuildNumber', c_uint16), > + ('VersionString', ARRAY(c_uint16, nums)), > + ] > + > + def ExtHeaderSize(self) -> int: > + return 2 * (nums+1) > + > + def GetVersionString(self) -> str: > + VersionString =3D '' > + for i in range(nums): > + if self.VersionString[i]: > + VersionString +=3D chr(self.VersionString[i]) > + return VersionString > + > + return EFI_SECTION_VERSION > diff --git a/BaseTools/Source/Python/FMMT/PI/__init__.py > b/BaseTools/Source/Python/FMMT/PI/__init__.py > new file mode 100644 > index 000000000000..4e8296fad1ba > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/PI/__init__.py > @@ -0,0 +1,6 @@ > +## @file > +# This file is used to define the FMMT dependent external tool managemen= t > class. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > \ No newline at end of file > diff --git a/BaseTools/Source/Python/FMMT/README.md > b/BaseTools/Source/Python/FMMT/README.md > new file mode 100644 > index 000000000000..e14dfa34ea66 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/README.md > @@ -0,0 +1,180 @@ > +# FMMT > +## Overview > +This FMMT tool is the python implementation of the edk2 FMMT tool which > locates at https://github.com/tianocore/edk2-staging/tree/FceFmmt. > +This implementation has the same usage as the edk2 FMMT, but it's more > readable and relaiable. > + > +# FMMT User Guide > + > +#### Last updated October 13, 2021 > + > +Important Changes and Updates: > + > +- Oct 13, 2021 Initial Draft of FMMT Python Tool > + > +#### Note: > + > +- FMMT Python Tool keeps same function with origin FMMT C Tool. It is > much easier to maintain and extend other functions. > + > + > + > +# 1. Introduction > + > +## 1.1 Overview > + > +The Firmware Device is a persistent physical repository that contains > firmware code and/or data. The firmware code and/or data stored in > Firmware Volumes. Detail layout of Firmware Volumes is described in ?Figu= re > 1. The Firmware Volume Format?. > + > +![](Img/FirmwareVolumeFormat.png) > + > +
Figure 1. The Firmware Volume Format
> + > +In firmware development, binary file has its firmware layout following t= he > Platform-Initialization Specification. Thus, operation on FV file / FFS f= ile > (Firmware File) is an efficient and convenient way for firmware function > testing and developing. FMMT Python tool is used for firmware files > operation. > + > +## 1.2 Tool Capabilities > + > +The FMMT tool is capable of: > + > +- Parse a FD (Firmware Device) / FV (Firmware Volume) / FFS (Firmware > Files) > + > +- Add a new FFS into a FV file (both included in a FD file or not) > + > +- Replace an FFS in a FV file with a new FFS file > + > +- Delete an FFS in a FV file (both included in a FD file or not) > + > +- Extract the FFS from a FV file (both includ-v < Inputfile > < Outputf= ile >ed > in a FD file or not) > + > +## 1.3 References > + > +| Document | > +| ------------------------------------------------ | > +| UEFI Platform Initialization (PI) Specification | > + > + > + > +# 2. FMMT Python Tool Usage > + > +## 2.1 Required Files > + > +### 2.1.1 Independent use > + > +When independent use the FMMT Python Tool, the following files and > settings are required: > + > +- GuidTool executable files used for Decompress/Compress Firmware data. > + > +- Environment variables path with GuidTool path setting. > + > +### 2.1.2 Use with Build System > + > +When use the FMMT Python Tool with Build System: > + > +- If only use Edk2 based GuidTool, do not need other preparation. > + > +- If use other customized GuidTool, need prepare the config file with > GuidTool info. The syntax for GuidTool definition shown as follow: > + > + ***ToolsGuid ShortName Command*** > + > + -- Example: ***3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI > BrotliCompress*** > + > +## 2.2 Syntax > + > +### 2.2.1 Syntax for Parse file > + > +***-v < Inputfile > < Outputfile > -l < LogFileType >*** > + > +- Parse *Inputfile*, show its firmware layout with log file. *Outputfile= * is > optional, if inputs, the *Inputfile* will be encapsulated into *Outputfil= e* > following the parsed firmware layout. *"-l LogFileType"* is optional, it > decides the format of log file which saves Binary layout. Currently suppo= rts: > json, txt. More formats will be added in the future. > + > +### 2.2.2 Syntax for Add a new FFS > + > +***-a < Inputfile > < TargetFvName/TargetFvGuid > < NewFfsFile > < > Outputfile >*** > + > +- Add the *NewFfsFile* into *Inputfile*. *TargetFvName/TargetFvGuid* > (Name or Guid) is the TargetFv which *NewFfsFile* will be added into. > + > +### 2.2.3 Syntax for Delete an FFS > + > +***-d < Inputfile > < TargetFfsName > < Outputfile > < > TargetFvName/TargetFvGuid >*** > + > +- Delete the Ffs from *Inputfile*. TargetFfsName (Guid) is the TargetFfs > which will be deleted. *TargetFvName/TargetFvGuid* is optional, which is > the parent of TargetFfs*.* > + > +### 2.2.4 Syntax for Replace an FFS > + > +***-r < Inputfile > < TargetFfsName > < NewFfsFile > < Outputfile > < > TargetFvName/TargetFvGuid >*** > + > +- Replace the Ffs with the NewFfsFile. TargetFfsName (Guid) is the Targe= tFfs > which will be replaced. *TargetFvName/TargetFvGuid* is optional, which is > the parent of TargetFfs*.* > + > +### 2.2.5 Syntax for Extract an FFS > + > +***-e < Inputfile > < TargetFfsName > < Outputfile >*** > + > +- Extract the Ffs from the Inputfile. TargetFfsName (Guid) is the Target= Ffs > which will be extracted. > + > + > + > +# 3. FMMT Python Tool Design > + > +FMMT Python Tool uses the NodeTree saves whole Firmware layout. Each > Node have its Data field, which saves the > FirmwareClass(FD/FV/FFS/SECTION/BINARY) Data. All the > parse/add/delete/replace/extract operations are based on the NodeTree > (adjusting the layout and data). > + > +## 3.1 NodeTree > + > +A whole NodeTree saves all the Firmware info. > + > +- Parent & Child relationship figured out the Firmware layout. > + > +- Each Node have several fields. ?Data? field saves an FirmwareClass > instance which contains all the data info of the info. > + > +### 3.1.1 NodeTree Format > + > +The NodeTree will be created with parse function. When parse a file, a R= oot > Node will be initialized firstly. The Data split and Tree construction pr= ocess is > described with an FD file shown as ?Figure 2. The NodeTree format?: > + > +- A Root Node is initialized. > + > +- Use the ?FV Signature? as FV key to split Whole FD > Data. ?FV0?, ?FV1?, ?FV2?? Node created. > + > +- After FV level Node created, use the ?Ffs Data Size? as FFS key to spl= it each > FV Data. ?Ffs0?...Node created. > + > +- After FFS level Node created, use the ?Section Data Size? as Section k= ey to > split each Ffs Data. ?Section0?...Node created. > + > +- If some of Section includes other Sections, continue use the ?Section = Data > Size? as Section key to split each Section Data. > + > +- After all Node created, the whole NodeTree saves all the info. (Can be > used in other functions or print the whole firmware layout into log file) > + > +![](Img/NodeTreeFormat.png) > + > +
Figure 2. The NodeTree format
> + > +### 3.1.2 Node Factory and Product > + > +As 3.1.1, Each Node is created by data split and recognition. To extend = the > NodeTree usage, Factory pattern is used in Node created process. > + > +Each Node have its Factory to create Product and use Product ParserData > function to deal with the data. > + > +## 3.2 GuidTool > + > +There are two ways to set the GuidTool. One from Config file, another fr= om > environment variables. > + > +Current GuidTool first check if has Config file. > + > +- If have, load the config GuidTool Information. > + > +- Else get from environment variables. > + > +### 3.2.1 Get from Config file > + > +- Config file should in same folder with FMMT.py or the path in > environment variables. > + > +- Content should follow the format: > + > + ***ToolsGuid ShortName Command*** > + > +### 3.2.2 Get from Environment Variables > + > +- The GuidTool Command used must be set in environment variables. > + > +### 3.2.3 Edk2 Based GuidTool > + > +| ***Guid*** | ***ShortName*** | ***Comm= and*** > | > +| ------------------------------------------ | --------------- | -------= -------------- | > +| ***a31280ad-481e-41b6-95e8-127f4c984779*** | ***TIANO*** | > ***TianoCompress*** | > +| ***ee4e5898-3914-4259-9d6e-dc7bd79403cf*** | ***LZMA*** | > ***LzmaCompress*** | > +| ***fc1bcdb0-7d31-49aa-936a-a4600d9dd083*** | ***CRC32*** | > ***GenCrc32*** | > +| ***d42ae6bd-1352-4bfb-909a-ca72a6eae889*** | ***LZMAF86*** | > ***LzmaF86Compress*** | > +| ***3d532050-5cda-4fd0-879e-0f7f630d5afb*** | ***BROTLI*** | > ***BrotliCompress*** | > diff --git a/BaseTools/Source/Python/FMMT/__init__.py > b/BaseTools/Source/Python/FMMT/__init__.py > new file mode 100644 > index 000000000000..f0e3e444ec86 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/__init__.py > @@ -0,0 +1,6 @@ > +## @file > +# This file is used to define the FMMT dependent external tool managemen= t > class. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > diff --git a/BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py > b/BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py > new file mode 100644 > index 000000000000..c7e341c18ace > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py > @@ -0,0 +1,371 @@ > +## @file > +# This file is used to implement of the various bianry parser. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from re import T > +import copy > +import os > +from PI.Common import * > +from core.BiosTreeNode import * > +from core.BiosTree import * > +from core.GuidTools import * > + > +ROOT_TREE =3D 'ROOT' > +ROOT_FV_TREE =3D 'ROOT_FV_TREE' > +ROOT_FFS_TREE =3D 'ROOT_FFS_TREE' > +ROOT_SECTION_TREE =3D 'ROOT_SECTION_TREE' > + > +FV_TREE =3D 'FV' > +DATA_FV_TREE =3D 'DATA_FV' > +FFS_TREE =3D 'FFS' > +FFS_PAD =3D 'FFS_PAD' > +FFS_FREE_SPACE =3D 'FFS_FREE_SPACE' > +SECTION_TREE =3D 'SECTION' > +SEC_FV_TREE =3D 'SEC_FV_IMAGE' > +BINARY_DATA =3D 'BINARY' > +Fv_count =3D 0 > + > +## Abstract factory > +class BinaryFactory(): > + type:list =3D [] > + > + def Create_Product(): > + pass > + > +class BinaryProduct(): > + ## Use GuidTool to decompress data. > + def DeCompressData(self, GuidTool, Section_Data: bytes) -> bytes: > + ParPath =3D > os.path.abspath(os.path.dirname(os.path.abspath(__file__))+os.path.sep+". > .") > + ToolPath =3D os.path.join(ParPath, r'FMMTConfig.ini') > + guidtool =3D GUIDTools(ToolPath).__getitem__(struct2stream(GuidT= ool)) > + DecompressedData =3D guidtool.unpack(Section_Data) > + return DecompressedData > + > + def ParserData(): > + pass > + > +class SectionFactory(BinaryFactory): > + type =3D [SECTION_TREE] > + > + def Create_Product(): > + return SectionProduct() > + > +class FfsFactory(BinaryFactory): > + type =3D [ROOT_SECTION_TREE, FFS_TREE] > + > + def Create_Product(): > + return FfsProduct() > + > +class FvFactory(BinaryFactory): > + type =3D [ROOT_FFS_TREE, FV_TREE, SEC_FV_TREE] > + > + def Create_Product(): > + return FvProduct() > + > +class FdFactory(BinaryFactory): > + type =3D [ROOT_FV_TREE, ROOT_TREE] > + > + def Create_Product(): > + return FdProduct() > + > +class SectionProduct(BinaryProduct): > + ## Decompress the compressed section. > + def ParserData(self, Section_Tree, whole_Data: bytes, Rel_Whole_Offs= et: > int=3D0) -> None: > + if Section_Tree.Data.Type =3D=3D 0x01: > + Section_Tree.Data.OriData =3D Section_Tree.Data.Data > + self.ParserFfs(Section_Tree, b'') > + # Guided Define Section > + elif Section_Tree.Data.Type =3D=3D 0x02: > + Section_Tree.Data.OriData =3D Section_Tree.Data.Data > + DeCompressGuidTool =3D > Section_Tree.Data.ExtHeader.SectionDefinitionGuid > + Section_Tree.Data.Data =3D > self.DeCompressData(DeCompressGuidTool, Section_Tree.Data.Data) > + Section_Tree.Data.Size =3D len(Section_Tree.Data.Data) + > Section_Tree.Data.HeaderLength > + self.ParserFfs(Section_Tree, b'') > + elif Section_Tree.Data.Type =3D=3D 0x03: > + Section_Tree.Data.OriData =3D Section_Tree.Data.Data > + self.ParserFfs(Section_Tree, b'') > + # SEC_FV Section > + elif Section_Tree.Data.Type =3D=3D 0x17: > + global Fv_count > + Sec_Fv_Info =3D FvNode(Fv_count, Section_Tree.Data.Data) > + Sec_Fv_Tree =3D BIOSTREE('FV'+ str(Fv_count)) > + Sec_Fv_Tree.type =3D SEC_FV_TREE > + Sec_Fv_Tree.Data =3D Sec_Fv_Info > + Sec_Fv_Tree.Data.HOffset =3D Section_Tree.Data.DOffset > + Sec_Fv_Tree.Data.DOffset =3D Sec_Fv_Tree.Data.HOffset + > Sec_Fv_Tree.Data.Header.HeaderLength > + Sec_Fv_Tree.Data.Data =3D > Section_Tree.Data.Data[Sec_Fv_Tree.Data.Header.HeaderLength:] > + Section_Tree.insertChild(Sec_Fv_Tree) > + Fv_count +=3D 1 > + > + def ParserFfs(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: in= t=3D0) > -> None: > + Rel_Offset =3D 0 > + Section_Offset =3D 0 > + # Get the Data from parent tree, if do not have the tree then ge= t it from > the whole_data. > + if ParTree.Data !=3D None: > + Data_Size =3D len(ParTree.Data.Data) > + Section_Offset =3D ParTree.Data.DOffset > + Whole_Data =3D ParTree.Data.Data > + else: > + Data_Size =3D len(Whole_Data) > + # Parser all the data to collect all the Section recorded in its= Parent > Section. > + while Rel_Offset < Data_Size: > + # Create a SectionNode and set it as the SectionTree's Data > + Section_Info =3D SectionNode(Whole_Data[Rel_Offset:]) > + Section_Tree =3D BIOSTREE(Section_Info.Name) > + Section_Tree.type =3D SECTION_TREE > + Section_Info.Data =3D > Whole_Data[Rel_Offset+Section_Info.HeaderLength: > Rel_Offset+Section_Info.Size] > + Section_Info.DOffset =3D Section_Offset + Section_Info.Heade= rLength + > Rel_Whole_Offset > + Section_Info.HOffset =3D Section_Offset + Rel_Whole_Offset > + Section_Info.ROffset =3D Rel_Offset > + if Section_Info.Header.Type =3D=3D 0: > + break > + # The final Section in parent Section does not need to add p= adding, > else must be 4-bytes align with parent Section start offset > + Pad_Size =3D 0 > + if (Rel_Offset+Section_Info.HeaderLength+len(Section_Info.Da= ta) !=3D > Data_Size): > + Pad_Size =3D GetPadSize(Section_Info.Size, 4) > + Section_Info.PadData =3D Pad_Size * b'\x00' > + if Section_Info.Header.Type =3D=3D 0x02: > + Section_Info.DOffset =3D Section_Offset + > Section_Info.ExtHeader.DataOffset + Rel_Whole_Offset > + Section_Info.Data =3D > Whole_Data[Rel_Offset+Section_Info.ExtHeader.DataOffset: > Rel_Offset+Section_Info.Size] > + if Section_Info.Header.Type =3D=3D 0x14: > + ParTree.Data.Version =3D Section_Info.ExtHeader.GetVersi= onString() > + if Section_Info.Header.Type =3D=3D 0x15: > + ParTree.Data.UiName =3D Section_Info.ExtHeader.GetUiStri= ng() > + Section_Offset +=3D Section_Info.Size + Pad_Size > + Rel_Offset +=3D Section_Info.Size + Pad_Size > + Section_Tree.Data =3D Section_Info > + ParTree.insertChild(Section_Tree) > + > +class FfsProduct(BinaryProduct): > + # ParserFFs / GetSection > + def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: > int=3D0) -> None: > + Rel_Offset =3D 0 > + Section_Offset =3D 0 > + # Get the Data from parent tree, if do not have the tree then ge= t it from > the whole_data. > + if ParTree.Data !=3D None: > + Data_Size =3D len(ParTree.Data.Data) > + Section_Offset =3D ParTree.Data.DOffset > + Whole_Data =3D ParTree.Data.Data > + else: > + Data_Size =3D len(Whole_Data) > + # Parser all the data to collect all the Section recorded in Ffs= . > + while Rel_Offset < Data_Size: > + # Create a SectionNode and set it as the SectionTree's Data > + Section_Info =3D SectionNode(Whole_Data[Rel_Offset:]) > + Section_Tree =3D BIOSTREE(Section_Info.Name) > + Section_Tree.type =3D SECTION_TREE > + Section_Info.Data =3D > Whole_Data[Rel_Offset+Section_Info.HeaderLength: > Rel_Offset+Section_Info.Size] > + Section_Info.DOffset =3D Section_Offset + Section_Info.Heade= rLength + > Rel_Whole_Offset > + Section_Info.HOffset =3D Section_Offset + Rel_Whole_Offset > + Section_Info.ROffset =3D Rel_Offset > + if Section_Info.Header.Type =3D=3D 0: > + break > + # The final Section in Ffs does not need to add padding, els= e must be > 4-bytes align with Ffs start offset > + Pad_Size =3D 0 > + if (Rel_Offset+Section_Info.HeaderLength+len(Section_Info.Da= ta) !=3D > Data_Size): > + Pad_Size =3D GetPadSize(Section_Info.Size, 4) > + Section_Info.PadData =3D Pad_Size * b'\x00' > + if Section_Info.Header.Type =3D=3D 0x02: > + Section_Info.DOffset =3D Section_Offset + > Section_Info.ExtHeader.DataOffset + Rel_Whole_Offset > + Section_Info.Data =3D > Whole_Data[Rel_Offset+Section_Info.ExtHeader.DataOffset: > Rel_Offset+Section_Info.Size] > + # If Section is Version or UI type, it saves the version and= UI info of its > parent Ffs. > + if Section_Info.Header.Type =3D=3D 0x14: > + ParTree.Data.Version =3D Section_Info.ExtHeader.GetVersi= onString() > + if Section_Info.Header.Type =3D=3D 0x15: > + ParTree.Data.UiName =3D Section_Info.ExtHeader.GetUiStri= ng() > + Section_Offset +=3D Section_Info.Size + Pad_Size > + Rel_Offset +=3D Section_Info.Size + Pad_Size > + Section_Tree.Data =3D Section_Info > + ParTree.insertChild(Section_Tree) > + > +class FvProduct(BinaryProduct): > + ## ParserFv / GetFfs > + def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: > int=3D0) -> None: > + Ffs_Offset =3D 0 > + Rel_Offset =3D 0 > + # Get the Data from parent tree, if do not have the tree then ge= t it from > the whole_data. > + if ParTree.Data !=3D None: > + Data_Size =3D len(ParTree.Data.Data) > + Ffs_Offset =3D ParTree.Data.DOffset > + Whole_Data =3D ParTree.Data.Data > + else: > + Data_Size =3D len(Whole_Data) > + # Parser all the data to collect all the Ffs recorded in Fv. > + while Rel_Offset < Data_Size: > + # Create a FfsNode and set it as the FFsTree's Data > + if Data_Size - Rel_Offset < 24: > + Ffs_Tree =3D BIOSTREE('Free_Space') > + Ffs_Tree.type =3D FFS_FREE_SPACE > + Ffs_Tree.Data =3D FreeSpaceNode(Whole_Data[Rel_Offset:]) > + Ffs_Tree.Data.HOffset =3D Ffs_Offset + Rel_Whole_Offset > + Ffs_Tree.Data.DOffset =3D Ffs_Tree.Data.HOffset > + ParTree.Data.Free_Space =3D Data_Size - Rel_Offset > + ParTree.insertChild(Ffs_Tree) > + Rel_Offset =3D Data_Size > + else: > + Ffs_Info =3D FfsNode(Whole_Data[Rel_Offset:]) > + Ffs_Tree =3D BIOSTREE(Ffs_Info.Name) > + Ffs_Info.HOffset =3D Ffs_Offset + Rel_Whole_Offset > + Ffs_Info.DOffset =3D Ffs_Offset + Ffs_Info.Header.Header= Length + > Rel_Whole_Offset > + Ffs_Info.ROffset =3D Rel_Offset > + if Ffs_Info.Name =3D=3D PADVECTOR: > + Ffs_Tree.type =3D FFS_PAD > + Ffs_Info.Data =3D > Whole_Data[Rel_Offset+Ffs_Info.Header.HeaderLength: > Rel_Offset+Ffs_Info.Size] > + Ffs_Info.Size =3D len(Ffs_Info.Data) + > Ffs_Info.Header.HeaderLength > + # if current Ffs is the final ffs of Fv and full of = b'\xff', define it > with Free_Space > + if struct2stream(Ffs_Info.Header).replace(b'\xff', b= '') =3D=3D b'': > + Ffs_Tree.type =3D FFS_FREE_SPACE > + Ffs_Info.Data =3D Whole_Data[Rel_Offset:] > + Ffs_Info.Size =3D len(Ffs_Info.Data) > + ParTree.Data.Free_Space =3D Ffs_Info.Size > + else: > + Ffs_Tree.type =3D FFS_TREE > + Ffs_Info.Data =3D > Whole_Data[Rel_Offset+Ffs_Info.Header.HeaderLength: > Rel_Offset+Ffs_Info.Size] > + # The final Ffs in Fv does not need to add padding, else= must be 8- > bytes align with Fv start offset > + Pad_Size =3D 0 > + if Ffs_Tree.type !=3D FFS_FREE_SPACE and > (Rel_Offset+Ffs_Info.Header.HeaderLength+len(Ffs_Info.Data) !=3D Data_Siz= e): > + Pad_Size =3D GetPadSize(Ffs_Info.Size, 8) > + Ffs_Info.PadData =3D Pad_Size * b'\xff' > + Ffs_Offset +=3D Ffs_Info.Size + Pad_Size > + Rel_Offset +=3D Ffs_Info.Size + Pad_Size > + Ffs_Tree.Data =3D Ffs_Info > + ParTree.insertChild(Ffs_Tree) > + > +class FdProduct(BinaryProduct): > + type =3D [ROOT_FV_TREE, ROOT_TREE] > + > + ## Create DataTree with first level /fv Info, then parser each Fv. > + def ParserData(self, WholeFvTree, whole_data: bytes=3Db'', offset: i= nt=3D0) -> > None: > + # Get all Fv image in Fd with offset and length > + Fd_Struct =3D self.GetFvFromFd(whole_data) > + data_size =3D len(whole_data) > + Binary_count =3D 0 > + global Fv_count > + # If the first Fv image is the Binary Fv, add it into the tree. > + if Fd_Struct[0][1] !=3D 0: > + Binary_node =3D BIOSTREE('BINARY'+ str(Binary_count)) > + Binary_node.type =3D BINARY_DATA > + Binary_node.Data =3D BinaryNode(str(Binary_count)) > + Binary_node.Data.Data =3D whole_data[:Fd_Struct[0][1]] > + Binary_node.Data.Size =3D len(Binary_node.Data.Data) > + Binary_node.Data.HOffset =3D 0 + offset > + WholeFvTree.insertChild(Binary_node) > + Binary_count +=3D 1 > + # Add the first collected Fv image into the tree. > + Cur_node =3D BIOSTREE(Fd_Struct[0][0]+ str(Fv_count)) > + Cur_node.type =3D Fd_Struct[0][0] > + Cur_node.Data =3D FvNode(Fv_count, > whole_data[Fd_Struct[0][1]:Fd_Struct[0][1]+Fd_Struct[0][2][0]]) > + Cur_node.Data.HOffset =3D Fd_Struct[0][1] + offset > + Cur_node.Data.DOffset =3D > Cur_node.Data.HOffset+Cur_node.Data.Header.HeaderLength > + Cur_node.Data.Data =3D > whole_data[Fd_Struct[0][1]+Cur_node.Data.Header.HeaderLength:Fd_Struct > [0][1]+Cur_node.Data.Size] > + WholeFvTree.insertChild(Cur_node) > + Fv_count +=3D 1 > + Fv_num =3D len(Fd_Struct) > + # Add all the collected Fv image and the Binary Fv image between= them > into the tree. > + for i in range(Fv_num-1): > + if Fd_Struct[i][1]+Fd_Struct[i][2][0] !=3D Fd_Struct[i+1][1]= : > + Binary_node =3D BIOSTREE('BINARY'+ str(Binary_count)) > + Binary_node.type =3D BINARY_DATA > + Binary_node.Data =3D BinaryNode(str(Binary_count)) > + Binary_node.Data.Data =3D > whole_data[Fd_Struct[i][1]+Fd_Struct[i][2][0]:Fd_Struct[i+1][1]] > + Binary_node.Data.Size =3D len(Binary_node.Data.Data) > + Binary_node.Data.HOffset =3D Fd_Struct[i][1]+Fd_Struct[i= ][2][0] + > offset > + WholeFvTree.insertChild(Binary_node) > + Binary_count +=3D 1 > + Cur_node =3D BIOSTREE(Fd_Struct[i+1][0]+ str(Fv_count)) > + Cur_node.type =3D Fd_Struct[i+1][0] > + Cur_node.Data =3D FvNode(Fv_count, > whole_data[Fd_Struct[i+1][1]:Fd_Struct[i+1][1]+Fd_Struct[i+1][2][0]]) > + Cur_node.Data.HOffset =3D Fd_Struct[i+1][1] + offset > + Cur_node.Data.DOffset =3D > Cur_node.Data.HOffset+Cur_node.Data.Header.HeaderLength > + Cur_node.Data.Data =3D > whole_data[Fd_Struct[i+1][1]+Cur_node.Data.Header.HeaderLength:Fd_Stru > ct[i+1][1]+Cur_node.Data.Size] > + WholeFvTree.insertChild(Cur_node) > + Fv_count +=3D 1 > + # If the final Fv image is the Binary Fv, add it into the tree > + if Fd_Struct[-1][1] + Fd_Struct[-1][2][0] !=3D data_size: > + Binary_node =3D BIOSTREE('BINARY'+ str(Binary_count)) > + Binary_node.type =3D BINARY_DATA > + Binary_node.Data =3D BinaryNode(str(Binary_count)) > + Binary_node.Data.Data =3D whole_data[Fd_Struct[-1][1]+Fd_Str= uct[- > 1][2][0]:] > + Binary_node.Data.Size =3D len(Binary_node.Data.Data) > + Binary_node.Data.HOffset =3D Fd_Struct[-1][1]+Fd_Struct[-1][= 2][0] + > offset > + WholeFvTree.insertChild(Binary_node) > + Binary_count +=3D 1 > + > + ## Get the first level Fv from Fd file. > + def GetFvFromFd(self, whole_data: bytes=3Db'') -> list: > + Fd_Struct =3D [] > + data_size =3D len(whole_data) > + cur_index =3D 0 > + # Get all the EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE FV image > offset and length. > + while cur_index < data_size: > + if EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE in > whole_data[cur_index:]: > + target_index =3D > whole_data[cur_index:].index(EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE) + > cur_index > + if whole_data[target_index+24:target_index+28] =3D=3D > FVH_SIGNATURE: > + Fd_Struct.append([FV_TREE, target_index - 16, unpack= ("Q", > whole_data[target_index+16:target_index+24])]) > + cur_index =3D Fd_Struct[-1][1] + Fd_Struct[-1][2][0] > + else: > + cur_index =3D target_index + 16 > + else: > + cur_index =3D data_size > + cur_index =3D 0 > + # Get all the EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE FV image > offset and length. > + while cur_index < data_size: > + if EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE in > whole_data[cur_index:]: > + target_index =3D > whole_data[cur_index:].index(EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE) + > cur_index > + if whole_data[target_index+24:target_index+28] =3D=3D > FVH_SIGNATURE: > + Fd_Struct.append([FV_TREE, target_index - 16, unpack= ("Q", > whole_data[target_index+16:target_index+24])]) > + cur_index =3D Fd_Struct[-1][1] + Fd_Struct[-1][2][0] > + else: > + cur_index =3D target_index + 16 > + else: > + cur_index =3D data_size > + cur_index =3D 0 > + # Get all the EFI_SYSTEM_NVDATA_FV_GUID_BYTE FV image offset and > length. > + while cur_index < data_size: > + if EFI_SYSTEM_NVDATA_FV_GUID_BYTE in whole_data[cur_index:]: > + target_index =3D > whole_data[cur_index:].index(EFI_SYSTEM_NVDATA_FV_GUID_BYTE) + > cur_index > + if whole_data[target_index+24:target_index+28] =3D=3D > FVH_SIGNATURE: > + Fd_Struct.append([DATA_FV_TREE, target_index - 16, > unpack("Q", whole_data[target_index+16:target_index+24])]) > + cur_index =3D Fd_Struct[-1][1] + Fd_Struct[-1][2][0] > + else: > + cur_index =3D target_index + 16 > + else: > + cur_index =3D data_size > + # Sort all the collect Fv image with offset. > + Fd_Struct.sort(key=3Dlambda x:x[1]) > + tmp_struct =3D copy.deepcopy(Fd_Struct) > + tmp_index =3D 0 > + Fv_num =3D len(Fd_Struct) > + # Remove the Fv image included in another Fv image. > + for i in range(1,Fv_num): > + if tmp_struct[i][1]+tmp_struct[i][2][0] < tmp_struct[i- > 1][1]+tmp_struct[i-1][2][0]: > + Fd_Struct.remove(Fd_Struct[i-tmp_index]) > + tmp_index +=3D 1 > + return Fd_Struct > + > +class ParserEntry(): > + FactoryTable:dict =3D { > + SECTION_TREE: SectionFactory, > + ROOT_SECTION_TREE: FfsFactory, > + FFS_TREE: FfsFactory, > + ROOT_FFS_TREE: FvFactory, > + FV_TREE: FvFactory, > + SEC_FV_TREE: FvFactory, > + ROOT_FV_TREE: FdFactory, > + ROOT_TREE: FdFactory, > + } > + > + def GetTargetFactory(self, Tree_type: str) -> BinaryFactory: > + if Tree_type in self.FactoryTable: > + return self.FactoryTable[Tree_type] > + > + def Generate_Product(self, TargetFactory: BinaryFactory, Tree, Data: > bytes, Offset: int) -> None: > + New_Product =3D TargetFactory.Create_Product() > + New_Product.ParserData(Tree, Data, Offset) > + > + def DataParser(self, Tree, Data: bytes, Offset: int) -> None: > + TargetFactory =3D self.GetTargetFactory(Tree.type) > + if TargetFactory: > + self.Generate_Product(TargetFactory, Tree, Data, Offset) > \ No newline at end of file > diff --git a/BaseTools/Source/Python/FMMT/core/BiosTree.py > b/BaseTools/Source/Python/FMMT/core/BiosTree.py > new file mode 100644 > index 000000000000..0b6252ecc1bb > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/BiosTree.py > @@ -0,0 +1,198 @@ > +## @file > +# This file is used to define the Bios layout tree structure and related > operations. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +import collections > +from PI.Common import * > + > +ROOT_TREE =3D 'ROOT' > +ROOT_FV_TREE =3D 'ROOT_FV_TREE' > +ROOT_FFS_TREE =3D 'ROOT_FFS_TREE' > +ROOT_SECTION_TREE =3D 'ROOT_SECTION_TREE' > + > +FV_TREE =3D 'FV' > +DATA_FV_TREE =3D 'DATA_FV' > +FFS_TREE =3D 'FFS' > +FFS_PAD =3D 'FFS_PAD' > +FFS_FREE_SPACE =3D 'FFS_FREE_SPACE' > +SECTION_TREE =3D 'SECTION' > +SEC_FV_TREE =3D 'SEC_FV_IMAGE' > +BINARY_DATA =3D 'BINARY' > + > +RootType =3D [ROOT_TREE, ROOT_FV_TREE, ROOT_FFS_TREE, > ROOT_SECTION_TREE] > +FvType =3D [FV_TREE, SEC_FV_TREE] > +FfsType =3D FFS_TREE > +SecType =3D SECTION_TREE > + > +class BIOSTREE: > + def __init__(self, NodeName: str) -> None: > + self.key =3D NodeName > + self.type =3D None > + self.Data =3D None > + self.Child =3D [] > + self.Findlist =3D [] > + self.Parent =3D None > + self.NextRel =3D None > + self.LastRel =3D None > + > + def HasChild(self) -> bool: > + if self.Child =3D=3D []: > + return False > + else: > + return True > + > + def isFinalChild(self) -> bool: > + ParTree =3D self.Parent > + if ParTree: > + if ParTree.Child[-1] =3D=3D self: > + return True > + return False > + > + # FvTree.insertChild() > + def insertChild(self, newNode, pos: int=3DNone) -> None: > + if len(self.Child) =3D=3D 0: > + self.Child.append(newNode) > + else: > + if not pos: > + LastTree =3D self.Child[-1] > + self.Child.append(newNode) > + LastTree.NextRel =3D newNode > + newNode.LastRel =3D LastTree > + else: > + newNode.NextRel =3D self.Child[pos-1].NextRel > + newNode.LastRel =3D self.Child[pos].LastRel > + self.Child[pos-1].NextRel =3D newNode > + self.Child[pos].LastRel =3D newNode > + self.Child.insert(pos, newNode) > + newNode.Parent =3D self > + > + # lastNode.insertRel(newNode) > + def insertRel(self, newNode) -> None: > + if self.Parent: > + parentTree =3D self.Parent > + new_index =3D parentTree.Child.index(self) + 1 > + parentTree.Child.insert(new_index, newNode) > + self.NextRel =3D newNode > + newNode.LastRel =3D self > + > + def deleteNode(self, deletekey: str) -> None: > + FindStatus, DeleteTree =3D self.FindNode(deletekey) > + if FindStatus: > + parentTree =3D DeleteTree.Parent > + lastTree =3D DeleteTree.LastRel > + nextTree =3D DeleteTree.NextRel > + if parentTree: > + index =3D parentTree.Child.index(DeleteTree) > + del parentTree.Child[index] > + if lastTree and nextTree: > + lastTree.NextRel =3D nextTree > + nextTree.LastRel =3D lastTree > + elif lastTree: > + lastTree.NextRel =3D None > + elif nextTree: > + nextTree.LastRel =3D None > + return DeleteTree > + else: > + print('Could not find the target tree') > + return None > + > + def FindNode(self, key: str, Findlist: list) -> None: > + if self.key =3D=3D key or (self.Data and self.Data.Name =3D=3D k= ey) or (self.type > =3D=3D FFS_TREE and self.Data.UiName =3D=3D key): > + Findlist.append(self) > + else: > + for item in self.Child: > + item.FindNode(key, Findlist) > + > + def GetTreePath(self): > + BiosTreePath =3D [self] > + while self.Parent: > + BiosTreePath.insert(0, self.Parent) > + self =3D self.Parent > + return BiosTreePath > + > + def parserTree(self, TargetDict: dict=3DNone, Info: list=3DNone, spa= ce: int=3D0, > ParFvId=3D"") -> None: > + Key =3D list(TargetDict.keys())[0] > + if TargetDict[Key]["Type"] in RootType: > + Info.append("Image File: {}".format(Key)) > + Info.append("FilesNum: > {}".format(TargetDict.get(Key).get('FilesNum'))) > + Info.append("\n") > + elif TargetDict[Key]["Type"] in FvType: > + space +=3D 2 > + if TargetDict[Key]["Type"] =3D=3D SEC_FV_TREE: > + Info.append("{}Child FV named {} of {}".format(space*" "= , Key, > ParFvId)) > + space +=3D 2 > + else: > + Info.append("FvId: {}".format(Key)) > + ParFvId =3D Key > + Info.append("{}FvNameGuid: {}".format(space*" ", > TargetDict.get(Key).get('FvNameGuid'))) > + Info.append("{}Attributes: {}".format(space*" ", > TargetDict.get(Key).get('Attributes'))) > + Info.append("{}Total Volume Size: {}".format(space*" ", > TargetDict.get(Key).get('Size'))) > + Info.append("{}Free Volume Size: {}".format(space*" ", > TargetDict.get(Key).get('FreeSize'))) > + Info.append("{}Volume Offset: {}".format(space*" ", > TargetDict.get(Key).get('Offset'))) > + Info.append("{}FilesNum: {}".format(space*" ", > TargetDict.get(Key).get('FilesNum'))) > + elif TargetDict[Key]["Type"] in FfsType: > + space +=3D 2 > + if TargetDict.get(Key).get('UiName') !=3D "b''": > + Info.append("{}File: {} / {}".format(space*" ", Key, > TargetDict.get(Key).get('UiName'))) > + else: > + Info.append("{}File: {}".format(space*" ", Key)) > + if "Files" in list(TargetDict[Key].keys()): > + for item in TargetDict[Key]["Files"]: > + self.parserTree(item, Info, space, ParFvId) > + > + def ExportTree(self,TreeInfo: dict=3DNone) -> dict: > + if TreeInfo is None: > + TreeInfo =3Dcollections.OrderedDict() > + > + if self.type =3D=3D ROOT_TREE or self.type =3D=3D ROOT_FV_TREE o= r self.type =3D=3D > ROOT_FFS_TREE or self.type =3D=3D ROOT_SECTION_TREE: > + key =3D str(self.key) > + TreeInfo[self.key] =3D collections.OrderedDict() > + TreeInfo[self.key]["Name"] =3D key > + TreeInfo[self.key]["Type"] =3D self.type > + TreeInfo[self.key]["FilesNum"] =3D len(self.Child) > + elif self.type =3D=3D FV_TREE or self.type =3D=3D SEC_FV_TREE: > + key =3D str(self.Data.FvId) > + TreeInfo[key] =3D collections.OrderedDict() > + TreeInfo[key]["Name"] =3D key > + if self.Data.FvId !=3D self.Data.Name: > + TreeInfo[key]["FvNameGuid"] =3D str(self.Data.Name) > + TreeInfo[key]["Type"] =3D self.type > + TreeInfo[key]["Attributes"] =3D hex(self.Data.Header.Attribu= tes) > + TreeInfo[key]["Size"] =3D hex(self.Data.Header.FvLength) > + TreeInfo[key]["FreeSize"] =3D hex(self.Data.Free_Space) > + TreeInfo[key]["Offset"] =3D hex(self.Data.HOffset) > + TreeInfo[key]["FilesNum"] =3D len(self.Child) > + elif self.type =3D=3D FFS_TREE: > + key =3D str(self.Data.Name) > + TreeInfo[key] =3D collections.OrderedDict() > + TreeInfo[key]["Name"] =3D key > + TreeInfo[key]["UiName"] =3D '{}'.format(self.Data.UiName) > + TreeInfo[key]["Version"] =3D '{}'.format(self.Data.Version) > + TreeInfo[key]["Type"] =3D self.type > + TreeInfo[key]["Size"] =3D hex(self.Data.Size) > + TreeInfo[key]["Offset"] =3D hex(self.Data.HOffset) > + TreeInfo[key]["FilesNum"] =3D len(self.Child) > + elif self.type =3D=3D SECTION_TREE and self.Data.Type =3D=3D 0x0= 2: > + key =3D str(self.Data.Name) > + TreeInfo[key] =3D collections.OrderedDict() > + TreeInfo[key]["Name"] =3D key > + TreeInfo[key]["Type"] =3D self.type > + TreeInfo[key]["Size"] =3D hex(len(self.Data.OriData) + > self.Data.HeaderLength) > + TreeInfo[key]["DecompressedSize"] =3D hex(self.Data.Size) > + TreeInfo[key]["Offset"] =3D hex(self.Data.HOffset) > + TreeInfo[key]["FilesNum"] =3D len(self.Child) > + elif self is not None: > + key =3D str(self.Data.Name) > + TreeInfo[key] =3D collections.OrderedDict() > + TreeInfo[key]["Name"] =3D key > + TreeInfo[key]["Type"] =3D self.type > + TreeInfo[key]["Size"] =3D hex(self.Data.Size) > + TreeInfo[key]["Offset"] =3D hex(self.Data.HOffset) > + TreeInfo[key]["FilesNum"] =3D len(self.Child) > + > + for item in self.Child: > + TreeInfo[key].setdefault('Files',[]).append( item.ExportTree= ()) > + > + return TreeInfo > \ No newline at end of file > diff --git a/BaseTools/Source/Python/FMMT/core/BiosTreeNode.py > b/BaseTools/Source/Python/FMMT/core/BiosTreeNode.py > new file mode 100644 > index 000000000000..cfb711eea5a2 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/BiosTreeNode.py > @@ -0,0 +1,191 @@ > +## @file > +# This file is used to define the BIOS Tree Node. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from PI.FvHeader import * > +from PI.FfsFileHeader import * > +from PI.SectionHeader import * > +from PI.Common import * > +import uuid > + > +SectionHeaderType =3D { > + 0x01:'EFI_COMPRESSION_SECTION', > + 0x02:'EFI_GUID_DEFINED_SECTION', > + 0x03:'EFI_SECTION_DISPOSABLE', > + 0x10:'EFI_SECTION_PE32', > + 0x11:'EFI_SECTION_PIC', > + 0x12:'EFI_SECTION_TE', > + 0x13:'EFI_SECTION_DXE_DEPEX', > + 0x14:'EFI_SECTION_VERSION', > + 0x15:'EFI_SECTION_USER_INTERFACE', > + 0x16:'EFI_SECTION_COMPATIBILITY16', > + 0x17:'EFI_SECTION_FIRMWARE_VOLUME_IMAGE', > + 0x18:'EFI_FREEFORM_SUBTYPE_GUID_SECTION', > + 0x19:'EFI_SECTION_RAW', > + 0x1B:'EFI_SECTION_PEI_DEPEX', > + 0x1C:'EFI_SECTION_MM_DEPEX' > +} > +HeaderType =3D [0x01, 0x02, 0x14, 0x15, 0x18] > + > +class BinaryNode: > + def __init__(self, name: str) -> None: > + self.Size =3D 0 > + self.Name =3D "BINARY" + str(name) > + self.HOffset =3D 0 > + self.Data =3D b'' > + > +class FvNode: > + def __init__(self, name, buffer: bytes) -> None: > + self.Header =3D > EFI_FIRMWARE_VOLUME_HEADER.from_buffer_copy(buffer) > + Map_num =3D (self.Header.HeaderLength - 56)//8 > + self.Header =3D Refine_FV_Header(Map_num).from_buffer_copy(buffe= r) > + self.FvId =3D "FV" + str(name) > + self.Name =3D "FV" + str(name) > + if self.Header.ExtHeaderOffset: > + self.ExtHeader =3D > EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer_copy(buffer[self.Heade > r.ExtHeaderOffset:]) > + self.Name =3D > uuid.UUID(bytes_le=3Dstruct2stream(self.ExtHeader.FvName)) > + self.ExtEntryOffset =3D self.Header.ExtHeaderOffset + 20 > + if self.ExtHeader.ExtHeaderSize !=3D 20: > + self.ExtEntryExist =3D 1 > + self.ExtEntry =3D > EFI_FIRMWARE_VOLUME_EXT_ENTRY.from_buffer_copy(buffer[self.ExtEntry > Offset:]) > + self.ExtTypeExist =3D 1 > + if self.ExtEntry.ExtEntryType =3D=3D 0x01: > + nums =3D (self.ExtEntry.ExtEntrySize - 8) // 16 > + self.ExtEntry =3D > Refine_FV_EXT_ENTRY_OEM_TYPE_Header(nums).from_buffer_copy(buffer[ > self.ExtEntryOffset:]) > + elif self.ExtEntry.ExtEntryType =3D=3D 0x02: > + nums =3D self.ExtEntry.ExtEntrySize - 20 > + self.ExtEntry =3D > Refine_FV_EXT_ENTRY_GUID_TYPE_Header(nums).from_buffer_copy(buffer > [self.ExtEntryOffset:]) > + elif self.ExtEntry.ExtEntryType =3D=3D 0x03: > + self.ExtEntry =3D > EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE.from_buffer_copy(b > uffer[self.ExtEntryOffset:]) > + else: > + self.ExtTypeExist =3D 0 > + else: > + self.ExtEntryExist =3D 0 > + self.Size =3D self.Header.FvLength > + self.HeaderLength =3D self.Header.HeaderLength > + self.HOffset =3D 0 > + self.DOffset =3D 0 > + self.ROffset =3D 0 > + self.Data =3D b'' > + if self.Header.Signature !=3D 1213613663: > + print('Invalid! Fv Header Signature {} is not > "_FVH".'.format(self.Header.Signature)) > + with open(str(self.Name)+'.fd', "wb") as f: > + f.write(struct2stream(self.Header)) > + assert False > + self.PadData =3D b'' > + self.Free_Space =3D 0 > + self.ModCheckSum() > + > + def ModCheckSum(self) -> None: > + # Fv Header Sums to 0. > + Header =3D struct2stream(self.Header)[::-1] > + Size =3D self.HeaderLength // 2 > + Sum =3D 0 > + for i in range(Size): > + Sum +=3D int(Header[i*2: i*2 + 2].hex(), 16) > + if Sum & 0xffff: > + self.Header.Checksum =3D int(hex(0x10000 - int(hex(Sum - > self.Header.Checksum)[-4:], 16)), 16) > + > + def ModFvExt(self) -> None: > + # If used space changes and self.ExtEntry.UsedSize exists, > self.ExtEntry.UsedSize need to be changed. > + if self.Header.ExtHeaderOffset and self.ExtEntryExist and > self.ExtTypeExist and self.ExtEntry.Hdr.ExtEntryType =3D=3D 0x03: > + self.ExtEntry.UsedSize =3D self.Header.FvLength - self.Free_= Space > + > + def ModFvSize(self) -> None: > + # If Fv Size changed, self.Header.FvLength and > self.Header.BlockMap[i].NumBlocks need to be changed. > + BlockMapNum =3D len(self.Header.BlockMap) > + for i in range(BlockMapNum): > + if self.Header.BlockMap[i].Length: > + self.Header.BlockMap[i].NumBlocks =3D self.Header.FvLeng= th // > self.Header.BlockMap[i].Length > + > + def ModExtHeaderData(self) -> None: > + if self.Header.ExtHeaderOffset: > + ExtHeaderData =3D struct2stream(self.ExtHeader) > + ExtHeaderDataOffset =3D self.Header.ExtHeaderOffset - > self.HeaderLength > + self.Data =3D self.Data[:ExtHeaderDataOffset] + ExtHeaderDat= a + > self.Data[ExtHeaderDataOffset+20:] > + if self.Header.ExtHeaderOffset and self.ExtEntryExist: > + ExtHeaderEntryData =3D struct2stream(self.ExtEntry) > + ExtHeaderEntryDataOffset =3D self.Header.ExtHeaderOffset + 2= 0 - > self.HeaderLength > + self.Data =3D self.Data[:ExtHeaderEntryDataOffset] + > ExtHeaderEntryData + > self.Data[ExtHeaderEntryDataOffset+len(ExtHeaderEntryData):] > + > +class FfsNode: > + def __init__(self, buffer: bytes) -> None: > + self.Header =3D EFI_FFS_FILE_HEADER.from_buffer_copy(buffer) > + # self.Attributes =3D unpack(" + if self.Header.FFS_FILE_SIZE !=3D 0 and self.Header.Attributes != =3D 0xff and > self.Header.Attributes & 0x01 =3D=3D 1: > + print('Error Ffs Header! Ffs Header Size and Attributes is n= ot > matched!') > + if self.Header.FFS_FILE_SIZE =3D=3D 0 and self.Header.Attributes= & 0x01 =3D=3D 1: > + self.Header =3D EFI_FFS_FILE_HEADER2.from_buffer_copy(buffer= ) > + self.Name =3D uuid.UUID(bytes_le=3Dstruct2stream(self.Header.Nam= e)) > + self.UiName =3D b'' > + self.Version =3D b'' > + self.Size =3D self.Header.FFS_FILE_SIZE > + self.HeaderLength =3D self.Header.HeaderLength > + self.HOffset =3D 0 > + self.DOffset =3D 0 > + self.ROffset =3D 0 > + self.Data =3D b'' > + self.PadData =3D b'' > + > + def ModCheckSum(self) -> None: > + HeaderData =3D struct2stream(self.Header) > + HeaderSum =3D 0 > + for item in HeaderData: > + HeaderSum +=3D item > + HeaderSum -=3D self.Header.State > + HeaderSum -=3D self.Header.IntegrityCheck.Checksum.File > + if HeaderSum & 0xff: > + Header =3D self.Header.IntegrityCheck.Checksum.Header + 0x10= 0 - > int(hex(HeaderSum)[-2:], 16) > + self.Header.IntegrityCheck.Checksum.Header =3D int(hex(Heade= r)[-2:], > 16) > + > +class SectionNode: > + def __init__(self, buffer: bytes) -> None: > + if buffer[0:3] !=3D b'\xff\xff\xff': > + self.Header =3D > EFI_COMMON_SECTION_HEADER.from_buffer_copy(buffer) > + else: > + self.Header =3D > EFI_COMMON_SECTION_HEADER2.from_buffer_copy(buffer) > + if self.Header.Type in SectionHeaderType: > + self.Name =3D SectionHeaderType[self.Header.Type] > + elif self.Header.Type =3D=3D 0: > + self.Name =3D "EFI_SECTION_RAW" > + else: > + self.Name =3D "SECTION" > + if self.Header.Type in HeaderType: > + self.ExtHeader =3D self.GetExtHeader(self.Header.Type, > buffer[self.Header.Common_Header_Size():], (self.Header.SECTION_SIZE- > self.Header.Common_Header_Size())) > + self.HeaderLength =3D self.Header.Common_Header_Size() + > self.ExtHeader.ExtHeaderSize() > + else: > + self.ExtHeader =3D None > + self.HeaderLength =3D self.Header.Common_Header_Size() > + self.Size =3D self.Header.SECTION_SIZE > + self.Type =3D self.Header.Type > + self.HOffset =3D 0 > + self.DOffset =3D 0 > + self.ROffset =3D 0 > + self.Data =3D b'' > + self.OriData =3D b'' > + self.OriHeader =3D b'' > + self.PadData =3D b'' > + > + def GetExtHeader(self, Type: int, buffer: bytes, nums: int=3D0) -> N= one: > + if Type =3D=3D 0x01: > + return EFI_COMPRESSION_SECTION.from_buffer_copy(buffer) > + elif Type =3D=3D 0x02: > + return EFI_GUID_DEFINED_SECTION.from_buffer_copy(buffer) > + elif Type =3D=3D 0x14: > + return Get_VERSION_Header((nums - > 2)//2).from_buffer_copy(buffer) > + elif Type =3D=3D 0x15: > + return > Get_USER_INTERFACE_Header(nums//2).from_buffer_copy(buffer) > + elif Type =3D=3D 0x18: > + return > EFI_FREEFORM_SUBTYPE_GUID_SECTION.from_buffer_copy(buffer) > + > +class FreeSpaceNode: > + def __init__(self, buffer: bytes) -> None: > + self.Name =3D 'Free_Space' > + self.Data =3D buffer > + self.Size =3D len(buffer) > + self.HOffset =3D 0 > + self.DOffset =3D 0 > + self.ROffset =3D 0 > + self.PadData =3D b'' > \ No newline at end of file > diff --git a/BaseTools/Source/Python/FMMT/core/FMMTOperation.py > b/BaseTools/Source/Python/FMMT/core/FMMTOperation.py > new file mode 100644 > index 000000000000..a0432c4093cf > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/FMMTOperation.py > @@ -0,0 +1,140 @@ > +## @file > +# This file is used to define the functions to operate bios binary file. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from core.FMMTParser import * > +from core.FvHandler import * > +from utils.FvLayoutPrint import * > + > +global Fv_count > +Fv_count =3D 0 > + > +# The ROOT_TYPE can be 'ROOT_TREE', 'ROOT_FV_TREE', 'ROOT_FFS_TREE', > 'ROOT_SECTION_TREE' > +def ParserFile(inputfile: str, ROOT_TYPE: str, logfile: str=3DNone, outp= utfile: > str=3DNone) -> None: > + # 1. Data Prepare > + with open(inputfile, "rb") as f: > + whole_data =3D f.read() > + FmmtParser =3D FMMTParser(inputfile, ROOT_TYPE) > + # 2. DataTree Create > + FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data) > + # 3. Log Output > + InfoDict =3D FmmtParser.WholeFvTree.ExportTree() > + FmmtParser.WholeFvTree.parserTree(InfoDict, FmmtParser.BinaryInfo) > + GetFormatter("").LogPrint(FmmtParser.BinaryInfo) > + if logfile: > + GetFormatter(logfile.lower()).dump(InfoDict, > FmmtParser.BinaryInfo,"Parser_Log_{}{}".format(os.path.basename(inputfile > ),".{}".format(logfile.lower()))) > + # 4. Data Encapsultion > + if outputfile: > + FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False) > + with open(outputfile, "wb") as f: > + f.write(FmmtParser.FinalData) > + > +def DeleteFfs(inputfile: str, TargetFfs_name: str, outputfile: str, Fv_n= ame: > str=3DNone) -> None: > + # 1. Data Prepare > + with open(inputfile, "rb") as f: > + whole_data =3D f.read() > + FmmtParser =3D FMMTParser(inputfile, ROOT_TREE) > + # 2. DataTree Create > + FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data) > + # 3. Data Modify > + FmmtParser.WholeFvTree.FindNode(TargetFfs_name, > FmmtParser.WholeFvTree.Findlist) > + # Choose the Specfic DeleteFfs with Fv info > + if Fv_name: > + for item in FmmtParser.WholeFvTree.Findlist: > + if item.Parent.key !=3D Fv_name and item.Parent.Data.Name != =3D > Fv_name: > + FmmtParser.WholeFvTree.Findlist.remove(item) > + if FmmtParser.WholeFvTree.Findlist !=3D []: > + for Delete_Ffs in FmmtParser.WholeFvTree.Findlist: > + FfsMod =3D FvHandler(None, Delete_Ffs) > + Status =3D FfsMod.DeleteFfs() > + else: > + print('Target Ffs not found!!!') > + # 4. Data Encapsultion > + if Status: > + FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False) > + with open(outputfile, "wb") as f: > + f.write(FmmtParser.FinalData) > + > +def AddNewFfs(inputfile: str, Fv_name: str, newffsfile: str, outputfile:= str) -> > None: > + # 1. Data Prepare > + with open(inputfile, "rb") as f: > + whole_data =3D f.read() > + FmmtParser =3D FMMTParser(inputfile, ROOT_TREE) > + # 2. DataTree Create > + FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data) > + # Get Target Fv and Target Ffs_Pad > + FmmtParser.WholeFvTree.FindNode(Fv_name, > FmmtParser.WholeFvTree.Findlist) > + # Create new ffs Tree > + with open(newffsfile, "rb") as f: > + new_ffs_data =3D f.read() > + NewFmmtParser =3D FMMTParser(newffsfile, ROOT_FFS_TREE) > + Status =3D False > + # 3. Data Modify > + if FmmtParser.WholeFvTree.Findlist: > + for TargetFv in FmmtParser.WholeFvTree.Findlist: > + TargetFfsPad =3D TargetFv.Child[-1] > + if TargetFfsPad.type =3D=3D FFS_FREE_SPACE: > + NewFmmtParser.ParserFromRoot(NewFmmtParser.WholeFvTree, > new_ffs_data, TargetFfsPad.Data.HOffset) > + else: > + NewFmmtParser.ParserFromRoot(NewFmmtParser.WholeFvTree, > new_ffs_data, TargetFfsPad.Data.HOffset+TargetFfsPad.Data.Size) > + FfsMod =3D FvHandler(NewFmmtParser.WholeFvTree.Child[0], > TargetFfsPad) > + Status =3D FfsMod.AddFfs() > + else: > + print('Target Fv not found!!!') > + # 4. Data Encapsultion > + if Status: > + FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False) > + with open(outputfile, "wb") as f: > + f.write(FmmtParser.FinalData) > + > +def ReplaceFfs(inputfile: str, Ffs_name: str, newffsfile: str, outputfil= e: str, > Fv_name: str=3DNone) -> None: > + # 1. Data Prepare > + with open(inputfile, "rb") as f: > + whole_data =3D f.read() > + FmmtParser =3D FMMTParser(inputfile, ROOT_TREE) > + # 2. DataTree Create > + FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data) > + with open(newffsfile, "rb") as f: > + new_ffs_data =3D f.read() > + newFmmtParser =3D FMMTParser(newffsfile, FV_TREE) > + newFmmtParser.ParserFromRoot(newFmmtParser.WholeFvTree, > new_ffs_data) > + Status =3D False > + # 3. Data Modify > + new_ffs =3D newFmmtParser.WholeFvTree.Child[0] > + new_ffs.Data.PadData =3D GetPadSize(new_ffs.Data.Size, 8) * b'\xff' > + FmmtParser.WholeFvTree.FindNode(Ffs_name, > FmmtParser.WholeFvTree.Findlist) > + if Fv_name: > + for item in FmmtParser.WholeFvTree.Findlist: > + if item.Parent.key !=3D Fv_name and item.Parent.Data.Name != =3D > Fv_name: > + FmmtParser.WholeFvTree.Findlist.remove(item) > + if FmmtParser.WholeFvTree.Findlist !=3D []: > + for TargetFfs in FmmtParser.WholeFvTree.Findlist: > + FfsMod =3D FvHandler(newFmmtParser.WholeFvTree.Child[0], > TargetFfs) > + Status =3D FfsMod.ReplaceFfs() > + else: > + print('Target Ffs not found!!!') > + # 4. Data Encapsultion > + if Status: > + FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False) > + with open(outputfile, "wb") as f: > + f.write(FmmtParser.FinalData) > + > +def ExtractFfs(inputfile: str, Ffs_name: str, outputfile: str) -> None: > + with open(inputfile, "rb") as f: > + whole_data =3D f.read() > + FmmtParser =3D FMMTParser(inputfile, ROOT_TREE) > + FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data) > + FmmtParser.WholeFvTree.FindNode(Ffs_name, > FmmtParser.WholeFvTree.Findlist) > + if FmmtParser.WholeFvTree.Findlist !=3D []: > + TargetNode =3D FmmtParser.WholeFvTree.Findlist[0] > + TargetFv =3D TargetNode.Parent > + if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY: > + TargetNode.Data.Header.State =3D c_uint8( > + ~TargetNode.Data.Header.State) > + FinalData =3D struct2stream(TargetNode.Data.Header) + > TargetNode.Data.Data > + with open(outputfile, "wb") as f: > + f.write(FinalData) > + else: > + print('Target Ffs not found!!!') > \ No newline at end of file > diff --git a/BaseTools/Source/Python/FMMT/core/FMMTParser.py > b/BaseTools/Source/Python/FMMT/core/FMMTParser.py > new file mode 100644 > index 000000000000..6e54b860a9c9 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/FMMTParser.py > @@ -0,0 +1,86 @@ > +## @file > +# This file is used to define the interface of Bios Parser. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +from PI.Common import * > +from core.BinaryFactoryProduct import ParserEntry > +from core.BiosTreeNode import * > +from core.BiosTree import * > +from core.GuidTools import * > + > +class FMMTParser: > + def __init__(self, name: str, TYPE: str) -> None: > + self.WholeFvTree =3D BIOSTREE(name) > + self.WholeFvTree.type =3D TYPE > + self.FinalData =3D b'' > + self.BinaryInfo =3D [] > + > + ## Parser the nodes in WholeTree. > + def ParserFromRoot(self, WholeFvTree=3DNone, whole_data: bytes=3Db''= , > Reloffset: int=3D0) -> None: > + if WholeFvTree.type =3D=3D ROOT_TREE or WholeFvTree.type =3D=3D > ROOT_FV_TREE: > + ParserEntry().DataParser(self.WholeFvTree, whole_data, Relof= fset) > + else: > + ParserEntry().DataParser(WholeFvTree, whole_data, Reloffset) > + for Child in WholeFvTree.Child: > + self.ParserFromRoot(Child, "") > + > + ## Encapuslation all the data in tree into self.FinalData > + def Encapsulation(self, rootTree, CompressStatus: bool) -> None: > + # If current node is Root node, skip it. > + if rootTree.type =3D=3D ROOT_TREE or rootTree.type =3D=3D ROOT_F= V_TREE or > rootTree.type =3D=3D ROOT_FFS_TREE or rootTree.type =3D=3D ROOT_SECTION_T= REE: > + print('Start at Root !') > + # If current node do not have Header, just add Data. > + elif rootTree.type =3D=3D BINARY_DATA or rootTree.type =3D=3D > FFS_FREE_SPACE: > + self.FinalData +=3D rootTree.Data.Data > + rootTree.Child =3D [] > + # If current node do not have Child and ExtHeader, just add its = Header > and Data. > + elif rootTree.type =3D=3D DATA_FV_TREE or rootTree.type =3D=3D F= FS_PAD: > + self.FinalData +=3D struct2stream(rootTree.Data.Header) + > rootTree.Data.Data + rootTree.Data.PadData > + if rootTree.isFinalChild(): > + ParTree =3D rootTree.Parent > + if ParTree.type !=3D 'ROOT': > + self.FinalData +=3D ParTree.Data.PadData > + rootTree.Child =3D [] > + # If current node is not Section node and may have Child and > ExtHeader, add its Header,ExtHeader. If do not have Child, add its Data. > + elif rootTree.type =3D=3D FV_TREE or rootTree.type =3D=3D FFS_TR= EE or > rootTree.type =3D=3D SEC_FV_TREE: > + if rootTree.HasChild(): > + self.FinalData +=3D struct2stream(rootTree.Data.Header) > + else: > + self.FinalData +=3D struct2stream(rootTree.Data.Header) = + > rootTree.Data.Data + rootTree.Data.PadData > + if rootTree.isFinalChild(): > + ParTree =3D rootTree.Parent > + if ParTree.type !=3D 'ROOT': > + self.FinalData +=3D ParTree.Data.PadData > + # If current node is Section, need to consider its ExtHeader, Ch= ild and > Compressed Status. > + elif rootTree.type =3D=3D SECTION_TREE: > + # Not compressed section > + if rootTree.Data.OriData =3D=3D b'' or (rootTree.Data.OriDat= a !=3D b'' and > CompressStatus): > + if rootTree.HasChild(): > + if rootTree.Data.ExtHeader: > + self.FinalData +=3D struct2stream(rootTree.Data.= Header) + > struct2stream(rootTree.Data.ExtHeader) > + else: > + self.FinalData +=3D struct2stream(rootTree.Data.= Header) > + else: > + Data =3D rootTree.Data.Data > + if rootTree.Data.ExtHeader: > + self.FinalData +=3D struct2stream(rootTree.Data.= Header) + > struct2stream(rootTree.Data.ExtHeader) + Data + rootTree.Data.PadData > + else: > + self.FinalData +=3D struct2stream(rootTree.Data.= Header) + Data > + rootTree.Data.PadData > + if rootTree.isFinalChild(): > + ParTree =3D rootTree.Parent > + self.FinalData +=3D ParTree.Data.PadData > + # If compressed section > + else: > + Data =3D rootTree.Data.OriData > + rootTree.Child =3D [] > + if rootTree.Data.ExtHeader: > + self.FinalData +=3D struct2stream(rootTree.Data.Head= er) + > struct2stream(rootTree.Data.ExtHeader) + Data + rootTree.Data.PadData > + else: > + self.FinalData +=3D struct2stream(rootTree.Data.Head= er) + Data + > rootTree.Data.PadData > + if rootTree.isFinalChild(): > + ParTree =3D rootTree.Parent > + self.FinalData +=3D ParTree.Data.PadData > + for Child in rootTree.Child: > + self.Encapsulation(Child, CompressStatus) > diff --git a/BaseTools/Source/Python/FMMT/core/FvHandler.py > b/BaseTools/Source/Python/FMMT/core/FvHandler.py > new file mode 100644 > index 000000000000..f67a73d0690c > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/FvHandler.py > @@ -0,0 +1,521 @@ > +## @file > +# This file is used to the implementation of Bios layout handler. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +import os > +from core.BiosTree import * > +from core.GuidTools import GUIDTools > +from core.BiosTreeNode import * > +from PI.Common import * > + > +EFI_FVB2_ERASE_POLARITY =3D 0x00000800 > + > +def ChangeSize(TargetTree, size_delta: int=3D0) -> None: > + if type(TargetTree.Data.Header) =3D=3D type(EFI_FFS_FILE_HEADER2()) = or > type(TargetTree.Data.Header) =3D=3D type(EFI_COMMON_SECTION_HEADER2()): > + TargetTree.Data.Size -=3D size_delta > + TargetTree.Data.Header.ExtendedSize -=3D size_delta > + elif TargetTree.type =3D=3D SECTION_TREE and TargetTree.Data.OriData= : > + OriSize =3D TargetTree.Data.Header.SECTION_SIZE > + OriSize -=3D size_delta > + TargetTree.Data.Header.Size[0] =3D OriSize % (16**2) > + TargetTree.Data.Header.Size[1] =3D OriSize % (16**4) //(16**2) > + TargetTree.Data.Header.Size[2] =3D OriSize // (16**4) > + else: > + TargetTree.Data.Size -=3D size_delta > + TargetTree.Data.Header.Size[0] =3D TargetTree.Data.Size % (16**2= ) > + TargetTree.Data.Header.Size[1] =3D TargetTree.Data.Size % (16**4= ) > //(16**2) > + TargetTree.Data.Header.Size[2] =3D TargetTree.Data.Size // (16**= 4) > + > +def ModifyFfsType(TargetFfs) -> None: > + if type(TargetFfs.Data.Header) =3D=3D type(EFI_FFS_FILE_HEADER()) an= d > TargetFfs.Data.Size > 0xFFFFFF: > + ExtendSize =3D TargetFfs.Data.Header.FFS_FILE_SIZE + 8 > + New_Header =3D EFI_FFS_FILE_HEADER2() > + New_Header.Name =3D TargetFfs.Data.Header.Name > + New_Header.IntegrityCheck =3D TargetFfs.Data.Header.IntegrityChe= ck > + New_Header.Type =3D TargetFfs.Data.Header.Type > + New_Header.Attributes =3D TargetFfs.Data.Header.Attributes > + New_Header.Size =3D 0 > + New_Header.State =3D TargetFfs.Data.Header.State > + New_Header.ExtendedSize =3D ExtendSize > + TargetFfs.Data.Header =3D New_Header > + TargetFfs.Data.Size =3D TargetFfs.Data.Header.FFS_FILE_SIZE > + TargetFfs.Data.HeaderLength =3D TargetFfs.Data.Header.HeaderLeng= th > + TargetFfs.Data.ModCheckSum() > + elif type(TargetFfs.Data.Header) =3D=3D type(EFI_FFS_FILE_HEADER2())= and > TargetFfs.Data.Size <=3D 0xFFFFFF: > + New_Header =3D EFI_FFS_FILE_HEADER() > + New_Header.Name =3D TargetFfs.Data.Header.Name > + New_Header.IntegrityCheck =3D TargetFfs.Data.Header.IntegrityChe= ck > + New_Header.Type =3D TargetFfs.Data.Header.Type > + New_Header.Attributes =3D TargetFfs.Data.Header.Attributes > + New_Header.Size =3D TargetFfs.Data.HeaderLength + TargetFfs.Data= .Size > + New_Header.State =3D TargetFfs.Data.Header.State > + TargetFfs.Data.Header =3D New_Header > + TargetFfs.Data.Size =3D TargetFfs.Data.Header.FFS_FILE_SIZE > + TargetFfs.Data.HeaderLength =3D TargetFfs.Data.Header.HeaderLeng= th > + TargetFfs.Data.ModCheckSum() > + if struct2stream(TargetFfs.Parent.Data.Header.FileSystemGuid) = =3D=3D > EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE: > + NeedChange =3D True > + for item in TargetFfs.Parent.Child: > + if type(item.Data.Header) =3D=3D type(EFI_FFS_FILE_HEADE= R2()): > + NeedChange =3D False > + if NeedChange: > + TargetFfs.Parent.Data.Header.FileSystemGuid =3D > ModifyGuidFormat("8c8ce578-8a3d-4f1c-9935-896185c32dd3") > + > + if type(TargetFfs.Data.Header) =3D=3D type(EFI_FFS_FILE_HEADER2()): > + TarParent =3D TargetFfs.Parent > + while TarParent: > + if TarParent.type =3D=3D FV_TREE and > struct2stream(TarParent.Data.Header.FileSystemGuid) =3D=3D > EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE: > + TarParent.Data.Header.FileSystemGuid =3D > ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A") > + TarParent =3D TarParent.Parent > + > +class FvHandler: > + def __init__(self, NewFfs, TargetFfs) -> None: > + self.NewFfs =3D NewFfs > + self.TargetFfs =3D TargetFfs > + self.Status =3D False > + self.Remain_New_Free_Space =3D 0 > + > + ## Use for Compress the Section Data > + def CompressData(self, TargetTree) -> None: > + TreePath =3D TargetTree.GetTreePath() > + pos =3D len(TreePath) > + self.Status =3D True > + while pos: > + if self.Status: > + if TreePath[pos-1].type =3D=3D SECTION_TREE and TreePath= [pos- > 1].Data.Type =3D=3D 0x02: > + self.CompressSectionData(TreePath[pos-1], None, Tree= Path[pos- > 1].Data.ExtHeader.SectionDefinitionGuid) > + else: > + if pos =3D=3D len(TreePath): > + self.CompressSectionData(TreePath[pos-1], pos) > + else: > + self.CompressSectionData(TreePath[pos-1], None) > + pos -=3D 1 > + > + def CompressSectionData(self, TargetTree, pos: int, GuidTool=3DNone)= -> > None: > + NewData =3D b'' > + temp_save_child =3D TargetTree.Child > + if TargetTree.Data: > + for item in temp_save_child: > + if item.type =3D=3D SECTION_TREE and not item.Data.OriDa= ta and > item.Data.ExtHeader: > + NewData +=3D struct2stream(item.Data.Header) + > struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData > + elif item.type =3D=3D SECTION_TREE and item.Data.OriData= and not > item.Data.ExtHeader: > + NewData +=3D struct2stream(item.Data.Header) + > item.Data.OriData + item.Data.PadData > + elif item.type =3D=3D SECTION_TREE and item.Data.OriData= and > item.Data.ExtHeader: > + NewData +=3D struct2stream(item.Data.Header) + > struct2stream(item.Data.ExtHeader) + item.Data.OriData + > item.Data.PadData > + elif item.type =3D=3D FFS_FREE_SPACE: > + NewData +=3D item.Data.Data + item.Data.PadData > + else: > + NewData +=3D struct2stream(item.Data.Header) + item.= Data.Data > + item.Data.PadData > + if TargetTree.type =3D=3D FFS_TREE: > + New_Pad_Size =3D GetPadSize(len(NewData), 8) > + Size_delta =3D len(NewData) - len(TargetTree.Data.Data) > + ChangeSize(TargetTree, -Size_delta) > + Delta_Pad_Size =3D len(TargetTree.Data.PadData) - New_Pa= d_Size > + self.Remain_New_Free_Space +=3D Delta_Pad_Size > + TargetTree.Data.PadData =3D b'\xff' * New_Pad_Size > + TargetTree.Data.ModCheckSum() > + elif TargetTree.type =3D=3D FV_TREE or TargetTree.type =3D= =3D SEC_FV_TREE > and not pos: > + if self.Remain_New_Free_Space: > + if TargetTree.Data.Free_Space: > + TargetTree.Data.Free_Space +=3D self.Remain_New_= Free_Space > + NewData +=3D self.Remain_New_Free_Space * b'\xff= ' > + TargetTree.Child[-1].Data.Data +=3D > self.Remain_New_Free_Space * b'\xff' > + else: > + TargetTree.Data.Data +=3D self.Remain_New_Free_S= pace * > b'\xff' > + New_Free_Space =3D BIOSTREE('FREE_SPACE') > + New_Free_Space.type =3D FFS_FREE_SPACE > + New_Free_Space.Data =3D FreeSpaceNode(b'\xff' * > self.Remain_New_Free_Space) > + TargetTree.insertChild(New_Free_Space) > + self.Remain_New_Free_Space =3D 0 > + if TargetTree.type =3D=3D SEC_FV_TREE: > + Size_delta =3D len(NewData) + self.Remain_New_Free_S= pace - > len(TargetTree.Data.Data) > + TargetTree.Data.Header.FvLength +=3D Size_delta > + TargetTree.Data.ModFvExt() > + TargetTree.Data.ModFvSize() > + TargetTree.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetTree) > + TargetTree.Data.ModCheckSum() > + elif TargetTree.type =3D=3D SECTION_TREE and TargetTree.Data= .Type !=3D > 0x02: > + New_Pad_Size =3D GetPadSize(len(NewData), 4) > + Size_delta =3D len(NewData) - len(TargetTree.Data.Data) > + ChangeSize(TargetTree, -Size_delta) > + if TargetTree.NextRel: > + Delta_Pad_Size =3D len(TargetTree.Data.PadData) - Ne= w_Pad_Size > + self.Remain_New_Free_Space +=3D Delta_Pad_Size > + TargetTree.Data.PadData =3D b'\x00' * New_Pad_Size > + TargetTree.Data.Data =3D NewData > + if GuidTool: > + ParPath =3D > os.path.abspath(os.path.dirname(os.path.abspath(__file__))+os.path.sep+". > .") > + ToolPath =3D os.path.join(ParPath, r'FMMTConfig.ini') > + guidtool =3D > GUIDTools(ToolPath).__getitem__(struct2stream(GuidTool)) > + CompressedData =3D guidtool.pack(TargetTree.Data.Data) > + if len(CompressedData) < len(TargetTree.Data.OriData): > + New_Pad_Size =3D GetPadSize(len(CompressedData), 4) > + Size_delta =3D len(CompressedData) - len(TargetTree.Data= .OriData) > + ChangeSize(TargetTree, -Size_delta) > + if TargetTree.NextRel: > + TargetTree.Data.PadData =3D b'\x00' * New_Pad_Size > + self.Remain_New_Free_Space =3D len(TargetTree.Data.O= riData) + > len(TargetTree.Data.PadData) - len(CompressedData) - New_Pad_Size > + else: > + TargetTree.Data.PadData =3D b'' > + self.Remain_New_Free_Space =3D len(TargetTree.Data.O= riData) - > len(CompressedData) > + TargetTree.Data.OriData =3D CompressedData > + elif len(CompressedData) =3D=3D len(TargetTree.Data.OriData)= : > + TargetTree.Data.OriData =3D CompressedData > + elif len(CompressedData) > len(TargetTree.Data.OriData): > + New_Pad_Size =3D GetPadSize(CompressedData, 4) > + self.Remain_New_Free_Space =3D len(CompressedData) + > New_Pad_Size - len(TargetTree.Data.OriData) - len(TargetTree.Data.PadData= ) > + Size_delta =3D len(TargetTree.Data.OriData) - len(Compre= ssedData) > + ChangeSize(TargetTree, -Size_delta) > + if TargetTree.NextRel: > + TargetTree.Data.PadData =3D b'\x00' * New_Pad_Size > + TargetTree.Data.OriData =3D CompressedData > + self.ModifyTest(TargetTree, self.Remain_New_Free_Space) > + self.Status =3D False > + > + def ModifyFvExtData(self, TreeNode) -> None: > + FvExtData =3D b'' > + if TreeNode.Data.Header.ExtHeaderOffset: > + FvExtHeader =3D struct2stream(TreeNode.Data.ExtHeader) > + FvExtData +=3D FvExtHeader > + if TreeNode.Data.Header.ExtHeaderOffset and > TreeNode.Data.ExtEntryExist: > + FvExtEntry =3D struct2stream(TreeNode.Data.ExtEntry) > + FvExtData +=3D FvExtEntry > + if FvExtData: > + InfoNode =3D TreeNode.Child[0] > + InfoNode.Data.Data =3D FvExtData + > InfoNode.Data.Data[TreeNode.Data.ExtHeader.ExtHeaderSize:] > + InfoNode.Data.ModCheckSum() > + > + def ModifyTest(self, ParTree, Needed_Space: int) -> None: > + if Needed_Space > 0: > + if ParTree.type =3D=3D FV_TREE or ParTree.type =3D=3D SEC_FV= _TREE: > + ParTree.Data.Data =3D b'' > + Needed_Space =3D Needed_Space - ParTree.Data.Free_Space > + if Needed_Space < 0: > + ParTree.Child[-1].Data.Data =3D b'\xff' * (-Needed_S= pace) > + ParTree.Data.Free_Space =3D (-Needed_Space) > + self.Status =3D True > + else: > + if ParTree.type =3D=3D FV_TREE: > + self.Status =3D False > + else: > + BlockSize =3D ParTree.Data.Header.BlockMap[0].Le= ngth > + New_Add_Len =3D BlockSize - Needed_Space%BlockSi= ze > + if New_Add_Len % BlockSize: > + ParTree.Child[-1].Data.Data =3D b'\xff' * Ne= w_Add_Len > + ParTree.Data.Free_Space =3D New_Add_Len > + Needed_Space +=3D New_Add_Len > + else: > + ParTree.Child.remove(ParTree.Child[-1]) > + ParTree.Data.Free_Space =3D 0 > + ParTree.Data.Size +=3D Needed_Space > + ParTree.Data.Header.Fvlength =3D ParTree.Data.Si= ze > + for item in ParTree.Child: > + if item.type =3D=3D FFS_FREE_SPACE: > + ParTree.Data.Data +=3D item.Data.Data + item.Dat= a.PadData > + else: > + ParTree.Data.Data +=3D struct2stream(item.Data.H= eader)+ > item.Data.Data + item.Data.PadData > + ParTree.Data.ModFvExt() > + ParTree.Data.ModFvSize() > + ParTree.Data.ModExtHeaderData() > + self.ModifyFvExtData(ParTree) > + ParTree.Data.ModCheckSum() > + elif ParTree.type =3D=3D FFS_TREE: > + ParTree.Data.Data =3D b'' > + for item in ParTree.Child: > + if item.Data.OriData: > + if item.Data.ExtHeader: > + ParTree.Data.Data +=3D struct2stream(item.Da= ta.Header) + > struct2stream(item.Data.ExtHeader) + item.Data.OriData + > item.Data.PadData > + else: > + ParTree.Data.Data +=3D struct2stream(item.Da= ta.Header)+ > item.Data.OriData + item.Data.PadData > + else: > + if item.Data.ExtHeader: > + ParTree.Data.Data +=3D struct2stream(item.Da= ta.Header) + > struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData > + else: > + ParTree.Data.Data +=3D struct2stream(item.Da= ta.Header)+ > item.Data.Data + item.Data.PadData > + ChangeSize(ParTree, -Needed_Space) > + New_Pad_Size =3D GetPadSize(ParTree.Data.Size, 8) > + Delta_Pad_Size =3D New_Pad_Size - len(ParTree.Data.PadDa= ta) > + Needed_Space +=3D Delta_Pad_Size > + ParTree.Data.PadData =3D b'\xff' * GetPadSize(ParTree.Da= ta.Size, 8) > + ParTree.Data.ModCheckSum() > + elif ParTree.type =3D=3D SECTION_TREE: > + OriData =3D ParTree.Data.Data > + ParTree.Data.Data =3D b'' > + for item in ParTree.Child: > + if item.type =3D=3D SECTION_TREE and item.Data.ExtHe= ader and > item.Data.Type !=3D 0x02: > + ParTree.Data.Data +=3D struct2stream(item.Data.H= eader) + > struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData > + elif item.type =3D=3D SECTION_TREE and item.Data.Ext= Header and > item.Data.Type =3D=3D 0x02: > + ParTree.Data.Data +=3D struct2stream(item.Data.H= eader) + > struct2stream(item.Data.ExtHeader) + item.Data.OriData + > item.Data.PadData > + else: > + ParTree.Data.Data +=3D struct2stream(item.Data.H= eader) + > item.Data.Data + item.Data.PadData > + if ParTree.Data.Type =3D=3D 0x02: > + ParTree.Data.Size +=3D Needed_Space > + ParPath =3D > os.path.abspath(os.path.dirname(os.path.abspath(__file__))) > + ToolPath =3D os.path.join(os.path.dirname(ParPath), > r'FMMTConfig.ini') > + guidtool =3D > GUIDTools(ToolPath).__getitem__(struct2stream(ParTree.Data.ExtHeader.Se > ctionDefinitionGuid)) > + CompressedData =3D guidtool.pack(ParTree.Data.Data) > + Needed_Space =3D len(CompressedData) - > len(ParTree.Data.OriData) > + ParTree.Data.OriData =3D CompressedData > + New_Size =3D ParTree.Data.HeaderLength + len(Compres= sedData) > + ParTree.Data.Header.Size[0] =3D New_Size % (16**2) > + ParTree.Data.Header.Size[1] =3D New_Size % (16**4) /= /(16**2) > + ParTree.Data.Header.Size[2] =3D New_Size // (16**4) > + if ParTree.NextRel: > + New_Pad_Size =3D GetPadSize(New_Size, 4) > + Delta_Pad_Size =3D New_Pad_Size - len(ParTree.Da= ta.PadData) > + ParTree.Data.PadData =3D b'\x00' * New_Pad_Size > + Needed_Space +=3D Delta_Pad_Size > + else: > + ParTree.Data.PadData =3D b'' > + elif Needed_Space: > + ChangeSize(ParTree, -Needed_Space) > + New_Pad_Size =3D GetPadSize(ParTree.Data.Size, 4) > + Delta_Pad_Size =3D New_Pad_Size - len(ParTree.Data.P= adData) > + Needed_Space +=3D Delta_Pad_Size > + ParTree.Data.PadData =3D b'\x00' * New_Pad_Size > + NewParTree =3D ParTree.Parent > + ROOT_TYPE =3D [ROOT_FV_TREE, ROOT_FFS_TREE, > ROOT_SECTION_TREE, ROOT_TREE] > + if NewParTree and NewParTree.type not in ROOT_TYPE: > + self.ModifyTest(NewParTree, Needed_Space) > + else: > + self.Status =3D True > + > + def ReplaceFfs(self) -> bool: > + TargetFv =3D self.TargetFfs.Parent > + # If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child = Ffs > Header State need be reversed. > + if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY: > + self.NewFfs.Data.Header.State =3D c_uint8( > + ~self.NewFfs.Data.Header.State) > + # NewFfs parsing will not calculate the PadSize, thus recalculat= e. > + self.NewFfs.Data.PadData =3D b'\xff' * GetPadSize(self.NewFfs.Da= ta.Size, > 8) > + if self.NewFfs.Data.Size >=3D self.TargetFfs.Data.Size: > + Needed_Space =3D self.NewFfs.Data.Size + > len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - > len(self.TargetFfs.Data.PadData) > + # If TargetFv have enough free space, just move part of the = free > space to NewFfs. > + if TargetFv.Data.Free_Space >=3D Needed_Space: > + # Modify TargetFv Child info and BiosTree. > + TargetFv.Child[-1].Data.Data =3D b'\xff' * (TargetFv.Dat= a.Free_Space - > Needed_Space) > + TargetFv.Data.Free_Space -=3D Needed_Space > + Target_index =3D TargetFv.Child.index(self.TargetFfs) > + TargetFv.Child.remove(self.TargetFfs) > + TargetFv.insertChild(self.NewFfs, Target_index) > + # Modify TargetFv Header and ExtHeader info. > + TargetFv.Data.ModFvExt() > + TargetFv.Data.ModFvSize() > + TargetFv.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetFv) > + TargetFv.Data.ModCheckSum() > + # return the Status > + self.Status =3D True > + # If TargetFv do not have enough free space, need move part = of the > free space of TargetFv's parent Fv to TargetFv/NewFfs. > + else: > + if TargetFv.type =3D=3D FV_TREE: > + self.Status =3D False > + else: > + # Recalculate TargetFv needed space to keep it match= the > BlockSize setting. > + Needed_Space -=3D TargetFv.Data.Free_Space > + BlockSize =3D TargetFv.Data.Header.BlockMap[0].Lengt= h > + New_Add_Len =3D BlockSize - Needed_Space%BlockSize > + Target_index =3D TargetFv.Child.index(self.TargetFfs= ) > + if New_Add_Len % BlockSize: > + TargetFv.Child[-1].Data.Data =3D b'\xff' * New_A= dd_Len > + TargetFv.Data.Free_Space =3D New_Add_Len > + Needed_Space +=3D New_Add_Len > + TargetFv.insertChild(self.NewFfs, Target_index) > + TargetFv.Child.remove(self.TargetFfs) > + else: > + TargetFv.Child.remove(self.TargetFfs) > + TargetFv.Data.Free_Space =3D 0 > + TargetFv.insertChild(self.NewFfs) > + # Encapsulate the Fv Data for update. > + TargetFv.Data.Data =3D b'' > + for item in TargetFv.Child: > + if item.type =3D=3D FFS_FREE_SPACE: > + TargetFv.Data.Data +=3D item.Data.Data + ite= m.Data.PadData > + else: > + TargetFv.Data.Data +=3D struct2stream(item.D= ata.Header)+ > item.Data.Data + item.Data.PadData > + TargetFv.Data.Size +=3D Needed_Space > + # Modify TargetFv Data Header and ExtHeader info. > + TargetFv.Data.Header.FvLength =3D TargetFv.Data.Size > + TargetFv.Data.ModFvExt() > + TargetFv.Data.ModFvSize() > + TargetFv.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetFv) > + TargetFv.Data.ModCheckSum() > + # Start free space calculating and moving process. > + self.ModifyTest(TargetFv.Parent, Needed_Space) > + else: > + New_Free_Space =3D self.TargetFfs.Data.Size - self.NewFfs.Da= ta.Size > + # If TargetFv already have free space, move the new free spa= ce into it. > + if TargetFv.Data.Free_Space: > + TargetFv.Child[-1].Data.Data +=3D b'\xff' * New_Free_Spa= ce > + TargetFv.Data.Free_Space +=3D New_Free_Space > + Target_index =3D TargetFv.Child.index(self.TargetFfs) > + TargetFv.Child.remove(self.TargetFfs) > + TargetFv.insertChild(self.NewFfs, Target_index) > + self.Status =3D True > + # If TargetFv do not have free space, create free space for = Fv. > + else: > + New_Free_Space_Tree =3D BIOSTREE('FREE_SPACE') > + New_Free_Space_Tree.type =3D FFS_FREE_SPACE > + New_Free_Space_Tree.Data =3D FfsNode(b'\xff' * New_Free_= Space) > + TargetFv.Data.Free_Space =3D New_Free_Space > + TargetFv.insertChild(New_Free_Space) > + Target_index =3D TargetFv.Child.index(self.TargetFfs) > + TargetFv.Child.remove(self.TargetFfs) > + TargetFv.insertChild(self.NewFfs, Target_index) > + self.Status =3D True > + # Modify TargetFv Header and ExtHeader info. > + TargetFv.Data.ModFvExt() > + TargetFv.Data.ModFvSize() > + TargetFv.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetFv) > + TargetFv.Data.ModCheckSum() > + return self.Status > + > + def AddFfs(self) -> bool: > + # NewFfs parsing will not calculate the PadSize, thus recalculat= e. > + self.NewFfs.Data.PadData =3D b'\xff' * GetPadSize(self.NewFfs.Da= ta.Size, > 8) > + if self.TargetFfs.type =3D=3D FFS_FREE_SPACE: > + TargetLen =3D self.NewFfs.Data.Size + len(self.NewFfs.Data.P= adData) - > self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData) > + TargetFv =3D self.TargetFfs.Parent > + # If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Ch= ild Ffs > Header State need be reversed. > + if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY= : > + self.NewFfs.Data.Header.State =3D c_uint8( > + ~self.NewFfs.Data.Header.State) > + # If TargetFv have enough free space, just move part of the = free > space to NewFfs, split free space to NewFfs and new free space. > + if TargetLen < 0: > + self.Status =3D True > + self.TargetFfs.Data.Data =3D b'\xff' * (-TargetLen) > + TargetFv.Data.Free_Space =3D (-TargetLen) > + TargetFv.Data.ModFvExt() > + TargetFv.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetFv) > + TargetFv.Data.ModCheckSum() > + TargetFv.insertChild(self.NewFfs, -1) > + ModifyFfsType(self.NewFfs) > + elif TargetLen =3D=3D 0: > + self.Status =3D True > + TargetFv.Child.remove(self.TargetFfs) > + TargetFv.insertChild(self.NewFfs) > + ModifyFfsType(self.NewFfs) > + # If TargetFv do not have enough free space, need move part = of the > free space of TargetFv's parent Fv to TargetFv/NewFfs. > + else: > + if TargetFv.type =3D=3D FV_TREE: > + self.Status =3D False > + elif TargetFv.type =3D=3D SEC_FV_TREE: > + # Recalculate TargetFv needed space to keep it match= the > BlockSize setting. > + BlockSize =3D TargetFv.Data.Header.BlockMap[0].Lengt= h > + New_Add_Len =3D BlockSize - TargetLen%BlockSize > + if New_Add_Len % BlockSize: > + self.TargetFfs.Data.Data =3D b'\xff' * New_Add_L= en > + self.TargetFfs.Data.Size =3D New_Add_Len > + TargetLen +=3D New_Add_Len > + TargetFv.insertChild(self.NewFfs, -1) > + TargetFv.Data.Free_Space =3D New_Add_Len > + else: > + TargetFv.Child.remove(self.TargetFfs) > + TargetFv.insertChild(self.NewFfs) > + TargetFv.Data.Free_Space =3D 0 > + ModifyFfsType(self.NewFfs) > + TargetFv.Data.Data =3D b'' > + for item in TargetFv.Child: > + if item.type =3D=3D FFS_FREE_SPACE: > + TargetFv.Data.Data +=3D item.Data.Data + ite= m.Data.PadData > + else: > + TargetFv.Data.Data +=3D struct2stream(item.D= ata.Header)+ > item.Data.Data + item.Data.PadData > + # Encapsulate the Fv Data for update. > + TargetFv.Data.Size +=3D TargetLen > + TargetFv.Data.Header.FvLength =3D TargetFv.Data.Size > + TargetFv.Data.ModFvExt() > + TargetFv.Data.ModFvSize() > + TargetFv.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetFv) > + TargetFv.Data.ModCheckSum() > + # Start free space calculating and moving process. > + self.ModifyTest(TargetFv.Parent, TargetLen) > + else: > + # If TargetFv do not have free space, need directly move par= t of the > free space of TargetFv's parent Fv to TargetFv/NewFfs. > + TargetLen =3D self.NewFfs.Data.Size + len(self.NewFfs.Data.P= adData) > + TargetFv =3D self.TargetFfs.Parent > + if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY= : > + self.NewFfs.Data.Header.State =3D c_uint8( > + ~self.NewFfs.Data.Header.State) > + if TargetFv.type =3D=3D FV_TREE: > + self.Status =3D False > + elif TargetFv.type =3D=3D SEC_FV_TREE: > + BlockSize =3D TargetFv.Data.Header.BlockMap[0].Length > + New_Add_Len =3D BlockSize - TargetLen%BlockSize > + if New_Add_Len % BlockSize: > + New_Free_Space =3D BIOSTREE('FREE_SPACE') > + New_Free_Space.type =3D FFS_FREE_SPACE > + New_Free_Space.Data =3D FreeSpaceNode(b'\xff' * New_= Add_Len) > + TargetLen +=3D New_Add_Len > + TargetFv.Data.Free_Space =3D New_Add_Len > + TargetFv.insertChild(self.NewFfs) > + TargetFv.insertChild(New_Free_Space) > + else: > + TargetFv.insertChild(self.NewFfs) > + ModifyFfsType(self.NewFfs) > + TargetFv.Data.Data =3D b'' > + for item in TargetFv.Child: > + if item.type =3D=3D FFS_FREE_SPACE: > + TargetFv.Data.Data +=3D item.Data.Data + item.Da= ta.PadData > + else: > + TargetFv.Data.Data +=3D struct2stream(item.Data.= Header)+ > item.Data.Data + item.Data.PadData > + TargetFv.Data.Size +=3D TargetLen > + TargetFv.Data.Header.FvLength =3D TargetFv.Data.Size > + TargetFv.Data.ModFvExt() > + TargetFv.Data.ModFvSize() > + TargetFv.Data.ModExtHeaderData() > + self.ModifyFvExtData(TargetFv) > + TargetFv.Data.ModCheckSum() > + self.ModifyTest(TargetFv.Parent, TargetLen) > + return self.Status > + > + def DeleteFfs(self) -> bool: > + Delete_Ffs =3D self.TargetFfs > + Delete_Fv =3D Delete_Ffs.Parent > + Add_Free_Space =3D Delete_Ffs.Data.Size + len(Delete_Ffs.Data.Pa= dData) > + if Delete_Fv.Data.Free_Space: > + if Delete_Fv.type =3D=3D SEC_FV_TREE: > + Used_Size =3D Delete_Fv.Data.Size - Delete_Fv.Data.Free_= Space - > Add_Free_Space > + BlockSize =3D Delete_Fv.Data.Header.BlockMap[0].Length > + New_Free_Space =3D BlockSize - Used_Size % BlockSize > + self.Remain_New_Free_Space +=3D Delete_Fv.Data.Free_Spac= e + > Add_Free_Space - New_Free_Space > + Delete_Fv.Child[-1].Data.Data =3D New_Free_Space * b'\xf= f' > + Delete_Fv.Data.Free_Space =3D New_Free_Space > + else: > + Used_Size =3D Delete_Fv.Data.Size - Delete_Fv.Data.Free_= Space - > Add_Free_Space > + Delete_Fv.Child[-1].Data.Data +=3D Add_Free_Space * b'\x= ff' > + Delete_Fv.Data.Free_Space +=3D Add_Free_Space > + New_Free_Space =3D Delete_Fv.Data.Free_Space > + else: > + if Delete_Fv.type =3D=3D SEC_FV_TREE: > + Used_Size =3D Delete_Fv.Data.Size - Add_Free_Space > + BlockSize =3D Delete_Fv.Data.Header.BlockMap[0].Length > + New_Free_Space =3D BlockSize - Used_Size % BlockSize > + self.Remain_New_Free_Space +=3D Add_Free_Space - > New_Free_Space > + Add_Free_Space =3D New_Free_Space > + else: > + Used_Size =3D Delete_Fv.Data.Size - Add_Free_Space > + New_Free_Space =3D Add_Free_Space > + New_Free_Space_Info =3D FfsNode(Add_Free_Space * b'\xff') > + New_Free_Space_Info.Data =3D Add_Free_Space * b'\xff' > + New_Ffs_Tree =3D BIOSTREE(New_Free_Space_Info.Name) > + New_Ffs_Tree.type =3D FFS_FREE_SPACE > + New_Ffs_Tree.Data =3D New_Free_Space_Info > + Delete_Fv.insertChild(New_Ffs_Tree) > + Delete_Fv.Data.Free_Space =3D Add_Free_Space > + Delete_Fv.Child.remove(Delete_Ffs) > + Delete_Fv.Data.Header.FvLength =3D Used_Size + New_Free_Space > + Delete_Fv.Data.ModFvExt() > + Delete_Fv.Data.ModFvSize() > + Delete_Fv.Data.ModExtHeaderData() > + self.ModifyFvExtData(Delete_Fv) > + Delete_Fv.Data.ModCheckSum() > + self.CompressData(Delete_Fv) > + self.Status =3D True > + return self.Status > diff --git a/BaseTools/Source/Python/FMMT/core/GuidTools.py > b/BaseTools/Source/Python/FMMT/core/GuidTools.py > new file mode 100644 > index 000000000000..e21d8e0406ae > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/core/GuidTools.py > @@ -0,0 +1,152 @@ > +## @file > +# This file is used to define the FMMT dependent external tool managemen= t > class. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > +import glob > +import logging > +import os > +import shutil > +import sys > +import tempfile > +import uuid > +from PI.Common import * > +from utils.FmmtLogger import FmmtLogger as logger > +import subprocess > + > +def ExecuteCommand(cmd: list) -> None: > + subprocess.run(cmd,stdout=3Dsubprocess.DEVNULL) > + > +class GUIDTool: > + def __init__(self, guid: str, short_name: str, command: str) -> None= : > + self.guid: str =3D guid > + self.short_name: str =3D short_name > + self.command: str =3D command > + > + def pack(self, buffer: bytes) -> bytes: > + """ > + compress file. > + """ > + tool =3D self.command > + if tool: > + tmp =3D tempfile.mkdtemp(dir=3Dos.environ.get('tmp')) > + ToolInputFile =3D os.path.join(tmp, "pack_uncompress_sec_fil= e") > + ToolOuputFile =3D os.path.join(tmp, "pack_sec_file") > + try: > + file =3D open(ToolInputFile, "wb") > + file.write(buffer) > + file.close() > + command =3D [tool, '-e', '-o', ToolOuputFile, > + ToolInputFile] > + ExecuteCommand(command) > + buf =3D open(ToolOuputFile, "rb") > + res_buffer =3D buf.read() > + except Exception as msg: > + logger.error(msg) > + return "" > + else: > + buf.close() > + if os.path.exists(tmp): > + shutil.rmtree(tmp) > + return res_buffer > + else: > + logger.error( > + "Error parsing section: EFI_SECTION_GUID_DEFINED cannot = be > parsed at this time.") > + logger.info("Its GUID is: %s" % self.guid) > + return "" > + > + > + def unpack(self, buffer: bytes) -> bytes: > + """ > + buffer: remove common header > + uncompress file > + """ > + tool =3D self.command > + if tool: > + tmp =3D tempfile.mkdtemp(dir=3Dos.environ.get('tmp')) > + ToolInputFile =3D os.path.join(tmp, "unpack_sec_file") > + ToolOuputFile =3D os.path.join(tmp, "unpack_uncompress_sec_f= ile") > + try: > + file =3D open(ToolInputFile, "wb") > + file.write(buffer) > + file.close() > + command =3D [tool, '-d', '-o', ToolOuputFile, ToolInputF= ile] > + ExecuteCommand(command) > + buf =3D open(ToolOuputFile, "rb") > + res_buffer =3D buf.read() > + except Exception as msg: > + logger.error(msg) > + return "" > + else: > + buf.close() > + if os.path.exists(tmp): > + shutil.rmtree(tmp) > + return res_buffer > + else: > + logger.error("Error parsing section: EFI_SECTION_GUID_DEFINE= D > cannot be parsed at this time.") > + logger.info("Its GUID is: %s" % self.guid) > + return "" > + > +class GUIDTools: > + ''' > + GUIDTools is responsible for reading FMMTConfig.ini, verify the tool= s and > provide interfaces to access those tools. > + ''' > + default_tools =3D { > + struct2stream(ModifyGuidFormat("a31280ad-481e-41b6-95e8- > 127f4c984779")): GUIDTool("a31280ad-481e-41b6-95e8-127f4c984779", > "TIANO", "TianoCompress"), > + struct2stream(ModifyGuidFormat("ee4e5898-3914-4259-9d6e- > dc7bd79403cf")): GUIDTool("ee4e5898-3914-4259-9d6e-dc7bd79403cf", > "LZMA", "LzmaCompress"), > + struct2stream(ModifyGuidFormat("fc1bcdb0-7d31-49aa-936a- > a4600d9dd083")): GUIDTool("fc1bcdb0-7d31-49aa-936a-a4600d9dd083", > "CRC32", "GenCrc32"), > + struct2stream(ModifyGuidFormat("d42ae6bd-1352-4bfb-909a- > ca72a6eae889")): GUIDTool("d42ae6bd-1352-4bfb-909a-ca72a6eae889", > "LZMAF86", "LzmaF86Compress"), > + struct2stream(ModifyGuidFormat("3d532050-5cda-4fd0-879e- > 0f7f630d5afb")): GUIDTool("3d532050-5cda-4fd0-879e-0f7f630d5afb", > "BROTLI", "BrotliCompress"), > + } > + > + def __init__(self, tooldef_file: str=3DNone) -> None: > + self.dir =3D os.path.dirname(__file__) > + self.tooldef_file =3D tooldef_file if tooldef_file else os.path.= join( > + self.dir, "FMMTConfig.ini") > + self.tooldef =3D dict() > + self.load() > + > + def VerifyTools(self) -> None: > + """ > + Verify Tools and Update Tools path. > + """ > + path_env =3D os.environ.get("PATH") > + path_env_list =3D path_env.split(os.pathsep) > + path_env_list.append(os.path.dirname(__file__)) > + path_env_list =3D list(set(path_env_list)) > + for tool in self.tooldef.values(): > + cmd =3D tool.command > + if os.path.isabs(cmd): > + if not os.path.exists(cmd): > + print("Tool Not found %s" % cmd) > + else: > + for syspath in path_env_list: > + if glob.glob(os.path.join(syspath, cmd+"*")): > + break > + else: > + print("Tool Not found %s" % cmd) > + > + def load(self) -> None: > + if os.path.exists(self.tooldef_file): > + with open(self.tooldef_file, "r") as fd: > + config_data =3D fd.readlines() > + for line in config_data: > + try: > + guid, short_name, command =3D line.split() > + new_format_guid =3D > struct2stream(ModifyGuidFormat(guid.strip())) > + self.tooldef[new_format_guid] =3D GUIDTool( > + guid.strip(), short_name.strip(), command.strip(= )) > + except: > + print("GuidTool load error!") > + continue > + else: > + self.tooldef.update(self.default_tools) > + > + self.VerifyTools() > + > + def __getitem__(self, guid) -> None: > + return self.tooldef.get(guid) > + > + > +guidtools =3D GUIDTools() > diff --git a/BaseTools/Source/Python/FMMT/utils/FmmtLogger.py > b/BaseTools/Source/Python/FMMT/utils/FmmtLogger.py > new file mode 100644 > index 000000000000..18f0cff96e4b > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/utils/FmmtLogger.py > @@ -0,0 +1,18 @@ > +## @file > +# This file is used to define the Fmmt Logger. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > + > +## > + > +import logging > +import sys > + > +FmmtLogger =3D logging.getLogger('FMMT') > +FmmtLogger.setLevel(logging.INFO) > + > +lh=3Dlogging.StreamHandler(sys.stdout) > +lf=3Dlogging.Formatter("%(levelname)-8s: %(message)s") > +lh.setFormatter(lf) > +FmmtLogger.addHandler(lh) > \ No newline at end of file > diff --git a/BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py > b/BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py > new file mode 100644 > index 000000000000..1a6c67056266 > --- /dev/null > +++ b/BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py > @@ -0,0 +1,54 @@ > +## @file > +# This file is used to define the printer for Bios layout. > +# > +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > + > +def GetFormatter(layout_format: str): > + if layout_format =3D=3D 'json': > + return JsonFormatter() > + elif layout_format =3D=3D 'yaml': > + return YamlFormatter() > + elif layout_format =3D=3D 'html': > + return HtmlFormatter() > + else: > + return TxtFormatter() > + > +class Formatter(object): > + def dump(self, layoutdict, layoutlist, outputfile: str=3DNone) -> No= ne: > + raise NotImplemented > + > +class JsonFormatter(Formatter): > + def dump(self,layoutdict: dict, layoutlist: list, outputfile: str=3D= None) -> > None: > + try: > + import json > + except: > + TxtFormatter().dump(layoutdict, layoutlist, outputfile) > + return > + print(outputfile) > + if outputfile: > + with open(outputfile,"w") as fw: > + json.dump(layoutdict, fw, indent=3D2) > + else: > + print(json.dumps(layoutdict,indent=3D2)) > + > +class TxtFormatter(Formatter): > + def LogPrint(self,layoutlist: list) -> None: > + for item in layoutlist: > + print(item) > + print('\n') > + > + def dump(self,layoutdict: dict, layoutlist: list, outputfile: str=3D= None) -> > None: > + print(outputfile) > + with open(outputfile, "w") as f: > + for item in layoutlist: > + f.writelines(item + '\n') > + > +class YamlFormatter(Formatter): > + def dump(self,layoutdict, layoutlist, outputfile =3D None): > + TxtFormatter().dump(layoutdict, layoutlist, outputfile) > + > +class HtmlFormatter(Formatter): > + def dump(self,layoutdict, layoutlist, outputfile =3D None): > + TxtFormatter().dump(layoutdict, layoutlist, outputfile) > \ No newline at end of file > -- > 2.26.1.windows.1 >=20 >=20 >=20 >=20 >=20