From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: intel.com, ip: 134.134.136.24, mailfrom: zhijux.fan@intel.com) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by groups.io with SMTP; Tue, 28 May 2019 23:09:06 -0700 X-Amp-Result: UNSCANNABLE X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 May 2019 23:09:05 -0700 X-ExtLoop1: 1 Received: from fmsmsx103.amr.corp.intel.com ([10.18.124.201]) by orsmga003.jf.intel.com with ESMTP; 28 May 2019 23:09:05 -0700 Received: from fmsmsx156.amr.corp.intel.com (10.18.116.74) by FMSMSX103.amr.corp.intel.com (10.18.124.201) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 28 May 2019 23:09:05 -0700 Received: from shsmsx152.ccr.corp.intel.com (10.239.6.52) by fmsmsx156.amr.corp.intel.com (10.18.116.74) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 28 May 2019 23:09:04 -0700 Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.129]) by SHSMSX152.ccr.corp.intel.com ([169.254.6.18]) with mapi id 14.03.0415.000; Wed, 29 May 2019 14:09:02 +0800 From: "Fan, ZhijuX" To: "devel@edk2.groups.io" CC: "Gao, Liming" , "Feng, Bob C" Subject: [PATCH] BaseTools:add UniTool.py to Edk2\BaseTools\Scripts Thread-Topic: [PATCH] BaseTools:add UniTool.py to Edk2\BaseTools\Scripts Thread-Index: AdUV5NFgiPHjimoeT/KdgIAvm6jaWQ== Date: Wed, 29 May 2019 06:09:02 +0000 Message-ID: Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 11.0.600.7 dlp-reaction: no-action x-originating-ip: [10.239.127.40] MIME-Version: 1.0 Return-Path: zhijux.fan@intel.com X-Groupsio-MsgNum: 41572 Content-Type: multipart/mixed; boundary="_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF631CCSHSMSX101ccrcor_" Content-Language: en-US --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF631CCSHSMSX101ccrcor_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=3D1855 UniTool is one python script to generate UQI (Universal Question Identifier= ) unicode string for HII question PROMPT string. UQI string can be used to identify each HII question. This script can be run in both Py2 and Py3. Cc: Bob Feng Cc: Liming Gao Signed-off-by: Zhiju.Fan --- BaseTools/Scripts/UniTool.py | 490 +++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 490 insertions(+) create mode 100644 BaseTools/Scripts/UniTool.py diff --git a/BaseTools/Scripts/UniTool.py b/BaseTools/Scripts/UniTool.py new file mode 100644 index 0000000000..6faecb9e7e --- /dev/null +++ b/BaseTools/Scripts/UniTool.py @@ -0,0 +1,490 @@ +## @file +# +# Function will sync up UQI definitions with uni files based on vfi/vfr/hf= r/sd/sdi in the tree. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import re, sys, os, getopt, codecs, fnmatch + +# global variable declarations +questionError =3D False +uqiList =3D re.compile('^#string[ \t]+([A-Z_0-9]+)[ \t]+#language[ \t]+uqi= [ \t\r\n]+"(?:[x\S]{1,2})([0-9a-fA-F]{4,5})"',re.M).findall +allUqis =3D {} +stringDict =3D {} +GlobalVarId =3D {} +options =3D {} + +#********************************************************************** +# description: Prints help information +# +# arguments: none +# +# returns: none +# + +def usage () : + sys.exit("Syntax: %s [-b] [-u] [-l] [-x] [-h] [-d 'rootDirectory1'] [-d= 'rootDirectory2'] [-d 'rootDirectory3']... [-q e|w] \ +'rootDirectory0' 'uqiFile'|'uqiFileDirectory' ['excludedDirectory1'] ['exc= ludedDirectory2'] ['excludedDirectory3']...\n%s" % + (os.path.basename(sys.argv[0]), + """\nFunction will sync up UQI definitions with uni files based on v= fi/vfr/hfr/sd/sdi in the tree.\n +Required Arguments: + 'rootdirectory0' path to root directory + 'uqiFileDirectory' path to UQI file(UqiList.uni) + 'uqiFile' UQI file + +Options: + -h Show this help + -b Build option returns error if any new UQI needs as= signing + based on vfi/vfr/hfr/sd/sdi when no -u option is s= pecified + -u Create new UQIs that does not already exist in uqi= File for + any string requiring a UQI based on vfi/vfr/hfr/sd= /sdi + NOTE: 'uqiFile' cannot be readonly! + -l Language deletion option (keeps only English and u= qi) + moves all UQIs to 'uqiFile' + NOTE: Uni files cannot be readonly! + -x Exclude 'rootDirectory'/'excludedDirectory1' & + 'rootDirectory'/'excludedDirectory2'... from UQI l= ist build + NOTE: Cannot be the same as rootDirectory + -d Add multiple root directories to process + -q Print warning(w) or return error(e) if different H= II questions + are referring same string token + +Return error if any duplicated UQI string or value in UQI list or if no de= finition +for any string referred by HII question when -b or -u is specified + +NOTE: Options must be specified before parameters +""")) + +#********************************************************************** +# description: Get uni file encoding +# +# arguments: filename - name of uni file +# +# returns: utf-8 or utf-16 +# +def GetUniFileEncoding(filename): + # + # Detect Byte Order Mark at beginning of file. Default to UTF-8 + # + Encoding =3D 'utf-8' + + # + # Read file + # + try: + UniFile =3D open(filename, mode=3D'rb') + FileIn =3D UniFile.read() + UniFile.close() + except: + return Encoding + + if (FileIn.startswith(codecs.BOM_UTF16_BE) or FileIn.startswith(codecs.B= OM_UTF16_LE)): + Encoding =3D 'utf-16' + + return Encoding + +# rewrite function os.path.walk +def Walk(top, func, arg): + try: + names =3D os.listdir(top) + except os.error: + return + func(arg, top, names) + for name in names: + name =3D os.path.join(top, name) + if os.path.isdir(name): + Walk(name, func, arg) + +#********************************************************************** +# description: Parses commandline arguments and options +# Calls function processUni to build dictionary of strings +# Calls other functions according to user specified options +# +# arguments: argv - contains all input from command line +# - must contain path to root directory +# - may contain options -h, -u, -l, -b or -x before path +# +# returns: none +# +def main(argv) : +##### Read input arguments and options + global allUqis, uqiList, questionError + try: + opts, args =3D getopt.getopt(argv[1:], "hulbxd:q:") # each letter is a= n optional argument + except getopt.GetoptError: + usage() + try: + dirNameList =3D [args[0]] + QuestionOption =3D None + for eachOpt in opts: + if eachOpt[0] =3D=3D '-d': + dirNameList.append(eachOpt[1]) + if eachOpt[0] =3D=3D '-q': + QuestionOption =3D eachOpt[1] + if (QuestionOption !=3D "e") and (QuestionOption !=3D "w"): + print("\nERROR: invalid option value for -q option\n") + raise Exception + destname =3D args[1] + if len(args) > 2: + exDirList =3D args[2:] + except: + usage() + + UpdateUQIs =3D False + LangOption =3D False + BuildOption =3D False + ExcludeOption =3D False + exPathList =3D [] + + for o,a in opts: + if o =3D=3D "-h": + usage() + if o =3D=3D "-b": + BuildOption =3D True + if o =3D=3D "-u": + BuildOption =3D True + UpdateUQIs =3D True + if o =3D=3D "-l": + LangOption =3D True + if o =3D=3D "-x": + ExcludeOption =3D True + try: + for eachExDir in exDirList: + for eachRootDir in dirNameList: + if eachExDir =3D=3D eachRootDir: + print("\nERROR: excludedDirectory is same as rootDirectory\n= ") + raise Exception + exPathList.append(eachRootDir + os.sep + eachExDir) + except: + usage() + + global options + options =3D {'destname':destname, 'dirNameList':dirNameList, 'exPathList= ':exPathList, 'BuildOption':BuildOption, 'UpdateUQIs':UpdateUQIs, + 'LangOption':LangOption, 'ExcludeOption':ExcludeOption, 'Ques= tionOption':QuestionOption} + print("UQI file: %s" %destname) + for eachDirName in dirNameList: + Walk(eachDirName, processUni, None) + if questionError: + raise RuntimeError + return + if os.path.isdir(options['destname']): + destFileName =3D options['destname']+os.sep+'UqiList.uni' + else: + destFileName =3D options['destname'] + if os.path.exists(destFileName) and (destFileName not in list(allUqis.ke= ys())): + try: + Encoding =3D GetUniFileEncoding (destFileName) + destFile =3D codecs.open(destFileName, 'r+', Encoding) + destFileBuffer =3D destFile.read() + destFile.close() + except IOError as e: + print("ERROR: " + e.args[1]) + raise RuntimeError + return + allUqis[destFileName]=3D uqiList(destFileBuffer) + returnVal =3D 0 + if BuildOption: + returnVal =3D newUqi() + if (returnVal =3D=3D 1): + raise(RuntimeError, 'Please fix UQI ERROR(s) above before proceeding= .') + else: + print("No UQI issues detected\n") + return + +#********************************************************************** +# description: newUqi collects a list of all currently used uqi values in = the tree +# Halt build if any duplicated string or value in UQI list. +# If -u option was specified, creates new UQIs that does not +# already exist in uqiFile for any string requiring a UQI. +# +# arguments: none +# +# returns: 0 on success +# 1 on error - this should cause the build to halt +# + +syntax =3D "S" +syntaxRE =3D re.compile('#string[ \t]+[A-Z_0-9]+[ \t]+#language[ \t]+uqi[ = \t\r\n]+"([x\S]{1,2}).*',re.DOTALL).findall + +def newUqi(): + global options, GlobalVarId, allUqis, syntax, syntaxRE + uqiRange=3D[] + uqiStringList=3D[] + createUQI=3D[] + returnVal =3D 0 + BaseNumSpaces =3D 47 # Used to line up the UQI values in the resulting u= qiFile + + # Look for duplication in the current UQIs and collect current range of = UQIs + for path in allUqis.keys(): + for uqiString in allUqis[path]: # path contains the path and filename = of each uni file + #Checks for duplicated strings in UQI list + for tempString in uqiStringList: + if tempString =3D=3D uqiString[0]: + print("ERROR: UQI string %s was assigned more than once and will= cause corruption!" %uqiString[0]) + print("Delete one occurrence of the string and rerun tool.") + returnVal =3D 1 #halt build + + uqiStringList.append(uqiString[0]) + + #Checks for duplicated UQI values in UQI list + if int(uqiString[1],16) in uqiRange: + print("ERROR: UQI value %04x was assigned more than once and will = cause corruption!" %int(uqiString[1],16)) + print("Delete one occurrance of the UQI and rerun tool to create a= lternate value.") + returnVal =3D 1 #halt build + uqiRange.append(int(uqiString[1],16)) + + for stringValue in GlobalVarId.keys(): + stringFound =3D False + for path in stringDict.keys(): + for uniString in stringDict[path]: # path contains the path and file= name of each uni file + if (stringValue =3D=3D uniString): + stringFound =3D True + break + if not stringFound: + print("ERROR: No definition for %s referred by HII question" %(strin= gValue)) + returnVal =3D 1 #halt build + + # Require a UQI for any string in vfr/vfi files + for stringValue in GlobalVarId.keys(): + # Ignore strings defined as STRING_TOKEN(0) + if (stringValue !=3D "0"): + # Check if this string already exists in the UQI list + if (stringValue not in uqiStringList) and (stringValue not in create= UQI): + createUQI.append(stringValue) + if not options['UpdateUQIs']: + print("ERROR: No UQI for %s referred by HII question" %(stringVa= lue)) + returnVal =3D 1 # halt build after printing all error messages + + if (returnVal =3D=3D 1): + return returnVal + + # Update uqiFile with necessary UQIs + if options['UpdateUQIs'] and createUQI: + if os.path.isdir(options['destname']): + destFileName =3D options['destname']+os.sep+'UqiList.uni' + else: + destFileName =3D options['destname'] + try: + Encoding =3D GetUniFileEncoding (destFileName) + outputFile =3D codecs.open(destFileName, 'r+', Encoding) + platformUQI=3DoutputFile.read() + except IOError as e: + print("ERROR: " + e.args[1]) + if (e.args[0]=3D=3D2): + try: + outputFile =3D codecs.open(destFileName, 'w', Encoding) + print(destFileName + " did not exist. Creating new file.") + platformUQI=3D'' + except: + print("Error creating " + destFileName + ".") + return 1 + if (e.args[1]=3D=3D"Permission denied"): + print("\n%s is Readonly. You must uncheck the ReadOnly attibute t= o run the -u option.\n" %destFileName) + return 1 + + #Determines and sets the UQI number format + #TODO: there is probably a more elegant way to do this... + syntaxL =3D syntaxRE(platformUQI) + if len(syntaxL) !=3D 0: + syntax =3D syntaxL[0] + + # script is reading the file in and writing it back instead of appendi= ng because the codecs module + # automatically adds a BOM wherever you start writing. This caused bui= ld failure. + uqiRange.sort() + if (uqiRange =3D=3D []): + nextUqi =3D 0 + else: + nextUqi =3D uqiRange[len(uqiRange) - 1] + 1 + + for stringValue in createUQI: + print("%s will be assigned a new UQI value" %stringValue) + uqiRange.append(nextUqi) + # + # Lines up the UQI values in the resulting uqiFile + # + spaces =3D " "*(BaseNumSpaces - len(stringValue)) + platformUQI +=3D '#string %s%s #language uqi \"%s%04x\"\r\n' %(strin= gValue,spaces,syntax,nextUqi) + print("#string %s%s #language uqi \"%s%04X\"" %(stringValue, spaces= , syntax, nextUqi)) + nextUqi +=3D 1 + + outputFile.seek(0) + outputFile.write(platformUQI) + outputFile.close() + + return 0 + + +#********************************************************************** +# description: Parses each uni file to collect dictionary of strings +# Removes additional languages and overwrites current uni fil= es +# if -l option was specified +# +# arguments: path - directory location of file including file name +# filename - name of file to be modified +# +# returns: error string if failure occurred; +# none if completed sucessfully +# +# the following are global so that parsefile is quicker + +findUniString =3D re.compile('^#string[ \t]+([A-Z_0-9]+)(?:[ \t\r\n]+#lang= uage[ \t]+[a-zA-Z-]{2,5}[ \t\r\n]+".*"[ \t]*[\r]?[\n]?)*',re.M).findall + +otherLang =3D re.compile('^#string[ \t]+[A-Z_0-9]+(?:[ \t\r\n]+#language[ = \t]+[a-zA-Z-]{2,5}[ \t\r\n]+".*"[ \t]*[\r]?[\n]?)*',re.M).findall +eachLang =3D re.compile('[ \t\r\n]+#language[ \t]+([a-zA-Z-]{2,5})[ \t\r\n= ]+".*"[ \t]*[\r]?[\n]?').findall + +uqiStrings =3D re.compile('^#string[ \t]+[A-Z_0-9]+[ \t]+#language[ \t]+uq= i[ \t\r\n]+".*"[ \t]*[\r]?[\n]?',re.M) + +def parsefile(path,filename): + global options, stringDict, allUqis, uqiList, findUniString, otherLang, = eachLang, uqiStrings + + FullPath =3D path+os.sep+filename + + try: + UniEncoding =3D GetUniFileEncoding (FullPath) + UniFile =3D codecs.open(FullPath, 'r', UniEncoding) + databuffer =3D UniFile.read() + UniFile.close() + except: + return ("Error opening " + FullPath + " for reading.") + writeFile =3D False + + if os.path.isdir(options['destname']): + destFileName =3D options['destname']+os.sep+'UqiList.uni' + else: + destFileName =3D options['destname'] + + if options['LangOption']: + try: + UqiEncoding =3D GetUniFileEncoding (destFileName) + outputFile =3D codecs.open(destFileName, 'r+', UqiEncoding) + platformUQI=3DoutputFile.read() + except IOError as e: + print("ERROR: " + e.args[1]) + if (e.args[0]=3D=3D2): + try: + outputFile =3D codecs.open(destFileName, 'w', UqiEncoding) + print(destFileName + " did not exist. Creating new file.") + platformUQI=3D'' + except: + return ("Error creating " + destFileName + ".") + else: + return ("Error opening " + destFileName + " for appending.") + + if (filename!=3DdestFileName.split(os.sep)[-1]): + Uqis =3D re.findall(uqiStrings,databuffer) + if Uqis: + for uqi in Uqis: + platformUQI +=3D uqi + outputFile.seek(0) + outputFile.write(platformUQI) + outputFile.close() + databuffer =3D re.sub(uqiStrings, '', databuffer) + if Uqis: + writeFile =3D True + print("Deleted uqis from %s" %FullPath) + stringlist =3D otherLang(databuffer) + for stringfound in stringlist: + thisString =3D eachLang(stringfound) + for languageFound in thisString: + if ((languageFound !=3D 'en') and (languageFound !=3D 'en-US') a= nd (languageFound !=3D 'eng') and (languageFound !=3D 'uqi')): + databuffer =3D re.sub(re.escape(stringfound), '', databuffer) + writeFile =3D True + print("Deleted %s from %s" %(languageFound, FullPath)) + if (filename!=3DdestFileName.split(os.sep)[-1]): + #adding strings to dictionary + stringDict[r'%s' %FullPath]=3D findUniString(databuffer) + #adding UQIs to dictionary + allUqis[r'%s' %FullPath]=3D uqiList(databuffer) + + if writeFile: + try: + UniFile =3D codecs.open(FullPath, 'w', UniEncoding) + UniFile.write(databuffer) + UniFile.close() + except: + return ("Error opening " + FullPath + " for writing.") + return + +#********************************************************************** +# description: Searches tree for uni files +# Calls parsefile to collect dictionary of strings in each un= i file +# Calls searchVfiFile for each vfi or vfr file found +# +# arguments: argument list is built by os.path.walk function call +# arg - None +# dirname - directory location of files +# names - specific files to search in directory +# +# returns: none +# +def processUni(args,dirname,names) : + global options + #Remove excludedDirectory + if options['ExcludeOption']: + for eachExDir in options['exPathList']: + for dir in names: + if os.path.join(dirname,dir) =3D=3D eachExDir: + names.remove(dir) + + for entry in names: + FullPath =3D dirname+os.sep+entry + if fnmatch.fnmatch(FullPath,'*.uni'): + parsefile(dirname,entry) + if fnmatch.fnmatch(FullPath,'*.vf*'): + searchVfiFile(FullPath) + if fnmatch.fnmatch(FullPath,'*.sd'): + searchVfiFile(FullPath) + if fnmatch.fnmatch(FullPath,'*.sdi'): + searchVfiFile(FullPath) + if fnmatch.fnmatch(FullPath,'*.hfr'): + searchVfiFile(FullPath) + return + +#********************************************************************** +# description: Compose a dictionary of all strings that may need UQIs assi= gned +# to them and key is the string +# +# arguments: filename - name of file to search for strings +# +# returns: none +# + +#separate regexes for readability +stringGroups =3D re.compile('^[ \t]*(?:oneof|numeric|checkbox|orderedlist)= [ \t]+varid.+?(?:endoneof|endnumeric|endcheckbox|endorderedlist);', re.DOTA= LL|re.M).findall +stringVarIds =3D re.compile('[ \t]*(?:oneof|numeric|checkbox|orderedlist)[= \t]+varid[ \t]*=3D[ \t]*([a-zA-Z_0-9]+\.[a-zA-Z_0-9]+)').findall +stringTokens =3D re.compile('prompt[ \t]*=3D[ \t]*STRING_TOKEN[ \t]*\(([a-= zA-Z_0-9]+)\)').findall + +def searchVfiFile(filename) : + global options, GlobalVarId, stringGroups, stringVarIds, stringTokens, q= uestionError + try: + vfiFile =3D open(filename, 'r') + databuffer=3DvfiFile.read() + vfiFile.close() + + # Finds specified lines in file + vfiStringGroup =3D stringGroups(databuffer) + + # Searches for prompts within specified lines + for eachGroup in vfiStringGroup: + for eachString in stringTokens(eachGroup): + # Ignore strings defined as STRING_TOKEN(0), STRING_TOKEN(STR_EMPT= Y) or STRING_TOKEN(STR_NULL) + if (eachString !=3D "0") and (eachString !=3D "STR_EMPTY") and (ea= chString !=3D "STR_NULL"): + if eachString not in GlobalVarId: + GlobalVarId[eachString]=3DstringVarIds(eachGroup) + else: + if (GlobalVarId[eachString][0] !=3D stringVarIds(eachGroup)[0]= ): + if options['QuestionOption']: + if options['QuestionOption'] =3D=3D "e": + questionError =3D True + print("ERROR:"), + if options['QuestionOption'] =3D=3D "w": + print("WARNING:"), + print("%s referred by different HII questions(%s and %s)" = %(eachString, GlobalVarId[eachString][0], stringVarIds(eachGroup)[0])) + except: + print("Error opening file at %s for reading." %filename) + +if __name__ =3D=3D '__main__' : + sys.exit(main(sys.argv)) --=20 2.14.1.windows.1 --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF631CCSHSMSX101ccrcor_ Content-Disposition: attachment; filename="winmail.dat" Content-Transfer-Encoding: base64 Content-Type: application/ms-tnef; name="winmail.dat" eJ8+Iq4NAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAAEFDRUMwRDFC MEZFNjNFNDc4MkU5MkVERjJBNUMwOUNBAIgHAQ2ABAACAAAAAgACAAEFgAMADgAAAOMHBQAdAAYA CQACAAMAIAEBIIADAA4AAADjBwUAHQAGAAkAAgADACABAQiABwAYAAAASVBNLk1pY3Jvc29mdCBN YWlsLk5vdGUAMQgBBIABADsAAABbUEFUQ0hdIEJhc2VUb29sczphZGQgVW5pVG9vbC5weSB0byBF ZGsyXEJhc2VUb29sc1xTY3JpcHRzAM0UAQuAAQAhAAAAQUNFQzBEMUIwRkU2M0U0NzgyRTkyRURG MkE1QzA5Q0EAiAcBA5AGAAQqAAA0AAAAAgF/AAEAAABIAAAAPEZBRDBEN0UwQUUwRkE1NEQ5ODdG NkU3MjQzNUNBRkQ1MEFGNjMxQ0NAU0hTTVNYMTAxLmNjci5jb3JwLmludGVsLmNvbT4ACwAfDgEA AAACAQkQAQAAAEMgAAA/IAAAIFkAAExaRnUPQy17YQAKZmJpZAQAAGNjwHBnMTI1MgD+A0PwdGV4 dAH3AqQD4wIABGNoCsBzZXQwIO8HbQKDAFARTTIKgAa0AoCWfQqACMg7CWIxOQ7AvwnDFnIKMhZx AoAVYioJsHMJ8ASQYXQFsg5QA2Bzom8BgCBFeBHBbhgwXQZSdgSQF7YCEHIAwHR9CFBuGjEQIAXA BaAbZGSaIANSIBAiF7JcdgiQ5HdrC4BkNR1TBPAHQA0XcDAKcRfyYmttawZzAZAAICBCTV9C4EVH SU59CvwB8QvxER+wWjpoAkBwczrALy9idWd6AxALYCQudAcwbm8FoWUuAQWwZy9zaG93X0EiUS5j Z2k/DdA9qDE4NR3gbAuAZQqBpSUUVQMAVG8G8CAEAMYgAiAZ4HB5dCPQA6AbBPUYkCAYNRngVVFJ LCAoJeEaMXMHQCBR6wpQH2BpJxFJAQACMAaQ9QiRKSUFdQMABaABACcwnnQFEBnAHHAFsUhJKJAC cSl2UFJPTVBUvSuFLihjK5UeUAOgYhngfnUSABxgGJAlBQ3QKhN5VCBlANBoLDsuJQxU7mgmYQT1 LrVyKxAmUC7Rxm8m8C0AeTIgAHAcYEU0gDMxrUNjOh+wb1hiIEYJ8CvgPAbgYmkkQC5mNrFAC4AQ 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 VZ8zi1agAQIR4pr9f+g9Q2XkAR3QpVxGpqUN/437qSBRIbuhy/RokeKiuNP/ubpDJXFEH0OFYR7q ou9Jj++yx24iNKQdUHdcgHEQbzH/SwzZ33HhTbR1QU0s3P+8xHdxRW8nM1QoVMd+j0phSf8QMG4w EzYdUNGBc0FooA/wc7KwNgkoMIJxXMs18V/gRU1QVFl8oG4xXd98TlUoMIOPixJXqXhxIv4wqsB8 tGItXqdjL2Q4YJL/qsBZ///jV6kagMFBdVA9ifdnP3aiPYlbV6iS4Cm6WUl/an+5ELYQnCx2ymu/ bMRb/DBdeGJtP1mUcwFnL9+J/dhWUUEF2U+6jHZ/d4vhsvgiZSJ4X3C4QPuFb3jugYcVRVJST1I6 qsDeLH8PeX96j3uUd3wPf//gIldBUk42IYFvhl/+IpIQp3GjYS2xweL8oKNS8cDSSElJQOdO4IoR EmL1khApqPAlYhk9a3JNPxz/dD1umaY/gIioK73jDuGKET8ddqqDmFA7N0++aGFfX/fHspgg4bIn mCAA0FVgmCByJzvKc3ngMNwworAorZkSKJpCxTF2kYctxbDhiCUyLjE0nOCigDixNG934DAxiCWI JX19A2EwnvAAHwBCAAEAAAAYAAAARgBhAG4ALAAgAFoAaABpAGoAdQBYAAAAHwBlAAEAAAAqAAAA egBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAAfAGQAAQAAAAoAAABT AE0AVABQAAAAAAACAUEAAQAAAGQAAAAAAAAAgSsfpL6jEBmdbgDdAQ9UAgAAAIBGAGEAbgAsACAA WgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAu AGMAbwBtAAAAHwACXQEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMA bwBtAAAAAAAfAOVfAQAAADIAAABzAGkAcAA6AHoAaABpAGoAdQB4AC4AZgBhAG4AQABpAG4AdABl AGwALgBjAG8AbQAAAAAAHwAaDAEAAAAYAAAARgBhAG4ALAAgAFoAaABpAGoAdQBYAAAAHwAfDAEA AAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAAfAB4MAQAA AAoAAABTAE0AVABQAAAAAAACARkMAQAAAGQAAAAAAAAAgSsfpL6jEBmdbgDdAQ9UAgAAAIBGAGEA bgAsACAAWgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0 AGUAbAAuAGMAbwBtAAAAHwABXQEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUA bAAuAGMAbwBtAAAAAAAfAPg/AQAAABgAAABGAGEAbgAsACAAWgBoAGkAagB1AFgAAAAfACNAAQAA ACoAAAB6AGgAaQBqAHUAeAAuAGYAYQBuAEAAaQBuAHQAZQBsAC4AYwBvAG0AAAAAAB8AIkABAAAA CgAAAFMATQBUAFAAAAAAAAIB+T8BAAAAZAAAAAAAAACBKx+kvqMQGZ1uAN0BD1QCAAAAgEYAYQBu ACwAIABaAGgAaQBqAHUAWAAAAFMATQBUAFAAAAB6AGgAaQBqAHUAeAAuAGYAYQBuAEAAaQBuAHQA ZQBsAC4AYwBvAG0AAAAfAAldAQAAACoAAAB6AGgAaQBqAHUAeAAuAGYAYQBuAEAAaQBuAHQAZQBs AC4AYwBvAG0AAAAAAAsAQDoBAAAAHwAaAAEAAAASAAAASQBQAE0ALgBOAG8AdABlAAAAAAADAPE/ CQQAAAsAQDoBAAAAAwD9P+QEAAACAQswAQAAABAAAACs7A0bD+Y+R4LpLt8qXAnKAwAXAAEAAABA ADkAAFMKA+UV1QFAAAgwgKFLA+UV1QEfAACAhgMCAAAAAADAAAAAAAAARgEAAAAeAAAAYQBjAGMA ZQBwAHQAbABhAG4AZwB1AGEAZwBlAAAAAAABAAAADAAAAGUAbgAtAFUAUwAAAB8ANwABAAAAdgAA AFsAUABBAFQAQwBIAF0AIABCAGEAcwBlAFQAbwBvAGwAcwA6AGEAZABkACAAVQBuAGkAVABvAG8A bAAuAHAAeQAgAHQAbwAgAEUAZABrADIAXABCAGEAcwBlAFQAbwBvAGwAcwBcAFMAYwByAGkAcAB0 AHMAAAAAAB8APQABAAAAAgAAAAAAAAADADYAAAAAAAIBcQABAAAAFgAAAAHVFeTRYIjx44pqHk/y nYCAL5uo2lkAAB8AcAABAAAAdgAAAFsAUABBAFQAQwBIAF0AIABCAGEAcwBlAFQAbwBvAGwAcwA6 AGEAZABkACAAVQBuAGkAVABvAG8AbAAuAHAAeQAgAHQAbwAgAEUAZABrADIAXABCAGEAcwBlAFQA bwBvAGwAcwBcAFMAYwByAGkAcAB0AHMAAAAAAB8ANRABAAAAkAAAADwARgBBAEQAMABEADcARQAw AEEARQAwAEYAQQA1ADQARAA5ADgANwBGADYARQA3ADIANAAzADUAQwBBAEYARAA1ADAAQQBGADYA MwAxAEMAQwBAAFMASABTAE0AUwBYADEAMAAxAC4AYwBjAHIALgBjAG8AcgBwAC4AaQBuAHQAZQBs AC4AYwBvAG0APgAAAAMA3j+fTgAAQAAHMJG6PwPlFdUBAgELAAEAAAAQAAAArOwNGw/mPkeC6S7f KlwJygMAJgAAAAAAAgFHAAEAAAAzAAAAYz1VUzthPU1DSTtwPUludGVsO2w9U0hTTVNYMTAxLTE5 MDUyOTA2MDkwMlotMjcwMDQAAAIBEDABAAAARgAAAAAAAAAmd705O+w4SaSmFT3LpXtCBwD60Nfg rg+lTZh/bnJDXK/VAAAARBFeAACmk2g2iHZsS6dK0+tHmkxkAAAJVI+PAAAAAB8A+j8BAAAAGAAA AEYAYQBuACwAIABaAGgAaQBqAHUAWAAAAAMACVkBAAAAQAAAgAggBgAAAAAAwAAAAAAAAEYAAAAA v4UAAJA0JgLlFdUBCwAAgAggBgAAAAAAwAAAAAAAAEYAAAAAgoUAAAAAAAADAACACCAGAAAAAADA AAAAAAAARgAAAADrhQAACQQAAB8AAICGAwIAAAAAAMAAAAAAAABGAQAAABgAAABkAGwAcAAtAHAA cgBvAGQAdQBjAHQAAAABAAAAGgAAAGQAbABwAGUALQB3AGkAbgBkAG8AdwBzAAAAAAAfAACAhgMC AAAAAADAAAAAAAAARgEAAAAYAAAAZABsAHAALQB2AGUAcgBzAGkAbwBuAAAAAQAAABYAAAAxADEA LgAwAC4ANgAwADAALgA3AAAAAAAfAACAhgMCAAAAAADAAAAAAAAARgEAAAAaAAAAZABsAHAALQBy AGUAYQBjAHQAaQBvAG4AAAAAAAEAAAAUAAAAbgBvAC0AYQBjAHQAaQBvAG4AAAADAA00/T8AAB8A AICGAwIAAAAAAMAAAAAAAABGAQAAACAAAAB4AC0AbQBzAC0AaABhAHMALQBhAHQAdABhAGMAaAAA AAEAAAACAAAAAAAAAB8AAICGAwIAAAAAAMAAAAAAAABGAQAAACIAAAB4AC0AbwByAGkAZwBpAG4A YQB0AGkAbgBnAC0AaQBwAAAAAAABAAAAIAAAAFsAMQAwAC4AMgAzADkALgAxADIANwAuADQAMABd AAAAR1w= --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF631CCSHSMSX101ccrcor_--