From 8851406708cded56bab4cfb91aa668f9e66ed973 Mon Sep 17 00:00:00 2001 From: shuaikangzhou <863909694@qq.com> Date: Mon, 20 Nov 2023 22:30:31 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=9B=B4=E6=96=B0wx=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/workspace.xml | 188 +++++++++++------------- app/DataBase/hard_link.py | 78 ++++++++++ app/DataBase/output_pc.py | 3 + app/decrypt/get_wx_info.py | 14 +- app/person.py | 4 + app/ui_pc/mainview.py | 10 +- app/ui_pc/tool/pc_decrypt/decryptUi.py | 18 +-- app/ui_pc/tool/pc_decrypt/decryptUi.ui | 4 +- app/ui_pc/tool/pc_decrypt/pc_decrypt.py | 39 +++-- doc/images/path_select.png | Bin 0 -> 42945 bytes doc/电脑端使用教程.md | 3 +- readme.md | 4 +- 12 files changed, 226 insertions(+), 139 deletions(-) create mode 100644 app/DataBase/hard_link.py create mode 100644 doc/images/path_select.png diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 0dc18c4..ab978f1 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,13 +4,16 @@ - @@ -680,7 +674,8 @@ - @@ -699,24 +694,9 @@ file://$PROJECT_DIR$/app/decrypt/decrypt.py - 105 + 103 - - file://$PROJECT_DIR$/app/person.py - 100 - - - file://$PROJECT_DIR$/app/person.py - 98 - - - file://$PROJECT_DIR$/app/person.py - 99 - diff --git a/app/DataBase/hard_link.py b/app/DataBase/hard_link.py new file mode 100644 index 0000000..448199f --- /dev/null +++ b/app/DataBase/hard_link.py @@ -0,0 +1,78 @@ +import binascii +import os.path +import sqlite3 +import threading +import xml.etree.ElementTree as ET + +lock = threading.Lock() +DB = None +cursor = None +db_path = "./app/Database/Msg/HardLinkImage.db" +root_path = '/MsgAttach/' +if os.path.exists(db_path): + DB = sqlite3.connect(db_path, check_same_thread=False) + # '''创建游标''' + cursor = DB.cursor() + + +def init_database(): + global DB + global cursor + if not DB: + if os.path.exists(db_path): + DB = sqlite3.connect(db_path, check_same_thread=False) + # '''创建游标''' + cursor = DB.cursor() + + +def get_image_by_md5(md5: bytes): + sql = ''' + select Md5Hash,MD5,FileName,HardLinkImageID.Dir as DirName1,HardLinkImageID2.Dir as DirName2 + from HardLinkImageAttribute + join HardLinkImageID on HardLinkImageAttribute.DirID1 = HardLinkImageID.DirID + join HardLinkImageID as HardLinkImageID2 on HardLinkImageAttribute.DirID2 = HardLinkImageID2.DirID + where MD5 = ?; + ''' + try: + lock.acquire(True) + cursor.execute(sql, [md5, ]) + result = cursor.fetchone() + return result + finally: + lock.release() + + +def get_md5_from_xml(content): + # 解析XML + root = ET.fromstring(content) + # 提取md5的值 + md5_value = root.find(".//img").get("md5") + print(md5_value) + return md5_value + + +def get_image(content, thumb=True): + md5 = get_md5_from_xml(content) + # md5 = 'bc37a58c32cb203ee9ac587b068e5853' + result = get_image_by_md5(binascii.unhexlify(md5)) + if result: + print(result) + dir1 = result[3] + dir2 = result[4] + data_image = result[2] + dir0 = 'Thumb' if thumb else 'Image' + dat_image = os.path.join(root_path, dir1, dir0, dir2, data_image) + return dat_image + + +# 6b02292eecea118f06be3a5b20075afc_t + +if __name__ == '__main__': + msg_root_path = './Msg/' + db_path = "./Msg/HardLinkImage.db" + init_database() + content = '''\n\t\n\t\n\t\n\n''' + print(get_image(content)) + print(get_image(content, thumb=False)) + result = get_md5_from_xml(content) + print(result) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 34bf547..c1e3940 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -6,6 +6,9 @@ from PyQt5.QtCore import pyqtSignal, QThread from . import msg from ..log import log +if not os.path.exists('./data/聊天记录'): + os.mkdir('./data/聊天记录') + class Output(QThread): """ diff --git a/app/decrypt/get_wx_info.py b/app/decrypt/get_wx_info.py index 395110d..52f844f 100644 --- a/app/decrypt/get_wx_info.py +++ b/app/decrypt/get_wx_info.py @@ -65,7 +65,7 @@ def read_info(version_list, is_logging=False): if len(wechat_process) == 0: error = "[-] WeChat No Run" if is_logging: print(error) - return error + return -1 for process in wechat_process: tmp_rd = {} @@ -76,8 +76,9 @@ def read_info(version_list, is_logging=False): bias_list = version_list.get(tmp_rd['version'], None) if not isinstance(bias_list, list): error = f"[-] WeChat Current Version {tmp_rd['version']} Is Not Supported" - if is_logging: print(error) - return error + if is_logging: + print(error) + return -2 wechat_base_address = 0 for module in process.memory_maps(grouped=False): @@ -86,8 +87,9 @@ def read_info(version_list, is_logging=False): break if wechat_base_address == 0: error = f"[-] WeChat WeChatWin.dll Not Found" - if is_logging: print(error) - return error + if is_logging: + print(error) + return -3 Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid) @@ -128,7 +130,7 @@ def get_info(): with open(VERSION_LIST_PATH, "r", encoding="utf-8") as f: VERSION_LIST = json.load(f) - result = read_info(VERSION_LIST) # 读取微信信息 + result = read_info(VERSION_LIST, True) # 读取微信信息 return result diff --git a/app/person.py b/app/person.py index 32c8dd4..e52aa89 100644 --- a/app/person.py +++ b/app/person.py @@ -55,6 +55,10 @@ def singleton(cls): class MePC: def __init__(self): self.avatar = QPixmap(Icon.Default_avatar_path) + self.wxid = '' + self.wx_dir = '' + self.name = '' + self.mobile = '' def set_avatar(self, img_bytes): if not img_bytes: diff --git a/app/ui_pc/mainview.py b/app/ui_pc/mainview.py index 4826281..cf2005b 100644 --- a/app/ui_pc/mainview.py +++ b/app/ui_pc/mainview.py @@ -85,6 +85,11 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow): wxid = dic.get('wxid') if wxid: me = MePC() + me.wxid = dic.get('wxid') + me.name = dic.get('name') + me.mobile = dic.get('mobile') + me.wx_dir = dic.get('wx_dir') + self.set_my_info(wxid) else: QMessageBox.information( @@ -158,11 +163,6 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow): self.avatar.scaled(60, 60) me = MePC() me.set_avatar(img_bytes) - dic = { - 'wxid': wxid - } - with open('./app/data/info.json', 'w', encoding='utf-8') as f: - f.write(json.dumps(dic)) self.myavatar.setScaledContents(True) self.myavatar.setPixmap(self.avatar) diff --git a/app/ui_pc/tool/pc_decrypt/decryptUi.py b/app/ui_pc/tool/pc_decrypt/decryptUi.py index 3fed138..d4b0b8d 100644 --- a/app/ui_pc/tool/pc_decrypt/decryptUi.py +++ b/app/ui_pc/tool/pc_decrypt/decryptUi.py @@ -53,14 +53,14 @@ class Ui_Dialog(object): self.lineEdit = QtWidgets.QLineEdit(Dialog) self.lineEdit.setStyleSheet("background:transparent;\n" "\n" - "border-radius:5px;\n" - "border-top: 0px solid #b2e281;\n" - "border-bottom: 2px solid black;\n" - "border-right: 0px solid #b2e281;\n" - "border-left: 0px solid #b2e281;\n" + " border-radius:5px;\n" + " border-top: 0px solid #b2e281;\n" + " border-bottom: 2px solid black;\n" + " border-right: 0px solid #b2e281;\n" + " border-left: 0px solid #b2e281;\n" "\n" - " \n" - "border-style:outset\n" + "\n" + " border-style:outset\n" " ") self.lineEdit.setFrame(False) self.lineEdit.setObjectName("lineEdit") @@ -72,7 +72,7 @@ class Ui_Dialog(object): self.label_6.setObjectName("label_6") self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1) self.label_key = QtWidgets.QLabel(Dialog) - self.label_key.setMaximumSize(QtCore.QSize(200, 16777215)) + self.label_key.setMaximumSize(QtCore.QSize(400, 16777215)) self.label_key.setText("") self.label_key.setObjectName("label_key") self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1) @@ -101,7 +101,7 @@ class Ui_Dialog(object): self.label_8.setObjectName("label_8") self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1) self.label_db_dir = QtWidgets.QLabel(Dialog) - self.label_db_dir.setMaximumSize(QtCore.QSize(200, 300)) + self.label_db_dir.setMaximumSize(QtCore.QSize(400, 300)) self.label_db_dir.setText("") self.label_db_dir.setObjectName("label_db_dir") self.gridLayout.addWidget(self.label_db_dir, 6, 1, 1, 1) diff --git a/app/ui_pc/tool/pc_decrypt/decryptUi.ui b/app/ui_pc/tool/pc_decrypt/decryptUi.ui index dff82c5..9049e6c 100644 --- a/app/ui_pc/tool/pc_decrypt/decryptUi.ui +++ b/app/ui_pc/tool/pc_decrypt/decryptUi.ui @@ -127,7 +127,7 @@ - 200 + 400 16777215 @@ -189,7 +189,7 @@ - 200 + 400 300 diff --git a/app/ui_pc/tool/pc_decrypt/pc_decrypt.py b/app/ui_pc/tool/pc_decrypt/pc_decrypt.py index e7226e6..99cbb33 100644 --- a/app/ui_pc/tool/pc_decrypt/pc_decrypt.py +++ b/app/ui_pc/tool/pc_decrypt/pc_decrypt.py @@ -1,3 +1,4 @@ +import json import os.path import time import traceback @@ -35,11 +36,13 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): def get_info(self): try: result = get_wx_info.get_info() + print(result) if result == -1: QMessageBox.critical(self, "错误", "请登录微信") elif result == -2: QMessageBox.critical(self, "错误", "微信版本不匹配\n请更新微信版本为:3.9.8.15") - # print(result) + elif result == -3: + QMessageBox.critical(self, "错误", "WeChat WeChatWin.dll Not Found") else: self.ready = True self.info = result[0] @@ -52,7 +55,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): self.lineEdit.setFocus() self.checkBox.setChecked(True) self.get_wxidSignal.emit(self.info['wxid']) - if self.wx_dir and os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])): + if self.wx_dir and os.path.exists(os.path.join(self.wx_dir)): self.label_ready.setText('已就绪') except Exception as e: print(e) @@ -71,12 +74,16 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): directory = QtWidgets.QFileDialog.getExistingDirectory( self, "选取微信安装目录——能看到All Users文件夹", "C:/") # 起始路径 - if directory: - self.label_db_dir.setText(directory) - self.wx_dir = directory - self.checkBox_2.setChecked(True) - if self.ready: - self.label_ready.setText('已就绪') + db_dir = os.path.join(directory, 'Msg') + if not os.path.exists(db_dir): + QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾") + return + + self.label_db_dir.setText(directory) + self.wx_dir = directory + self.checkBox_2.setChecked(True) + if self.ready: + self.label_ready.setText('已就绪') def decrypt(self): if not self.ready: @@ -88,11 +95,12 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): if self.lineEdit.text() == 'None': QMessageBox.critical(self, "错误", "请填入wxid") return + db_dir = os.path.join(self.wx_dir, 'Msg') if self.ready: - if not os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])): - QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以WeChat Files结尾") + if not os.path.exists(db_dir): + QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾") return - db_dir = os.path.join(self.wx_dir, self.info['wxid'], 'Msg') + self.thread2 = DecryptThread(db_dir, self.info['key']) self.thread2.maxNumSignal.connect(self.setProgressBarMaxNum) self.thread2.signal.connect(self.progressBar_view) @@ -103,6 +111,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): # print("enter clicked") # 中间可以添加处理逻辑 # QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下") + self.DecryptSignal.emit('ok') # self.close() @@ -121,6 +130,14 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog): def btnExitClicked(self): # print("Exit clicked") + dic = { + 'wxid': self.info['wxid'], + 'wx_dir': self.wx_dir, + 'name': self.info['name'], + 'mobile': self.info['mobile'] + } + with open('./app/data/info.json', 'w', encoding='utf-8') as f: + f.write(json.dumps(dic)) self.DecryptSignal.emit('ok') self.close() diff --git a/doc/images/path_select.png b/doc/images/path_select.png new file mode 100644 index 0000000000000000000000000000000000000000..b8e7b1d448522532536cf4ccd8878aefb0f7569a GIT binary patch literal 42945 zcmb@ubyQr-*EWcR;GRHmcMI+ipn=9MSa1st!5xAH2=2iN?(QCfySoIZu@2fm(=gnd zd)K@(^ZwS%eBb*|t+TpM)u~fed+(>7diIG>RhGp>BSwRRgTs^q0@UH)5cA>S5ZF*& zKL0XSUh!N~!pQ+7-g{=AWT6}DYi2zKBwod*$TAtreXwKEN}}zTkuZ2C!Hhn=cW#t1 zcNCPd&D>z8^H}nLX_LUbY@wY>#}cb)tu-gjm7F~2pxW1nb|!gDFG)`slrqBy&#EfF zN~`qo@xbRyl-u%bO*qz^D{&BlIGffQ%YBgTW9cjofe0UbxdCrd;7g)Vh2T6F1)q~% zwIlp}`CNP?Pg48$AEYoY`G0Tvr7DB^=l+maq$7;~T*d`)kNRJ(jJEGFY__WzZd_ON zf0s1tU(#}U9}VPWBX4=Ns9?X_#i6oY7Y-)pYaot(<9=lEU>H;h&rb&Jh@ z+)bu{9m>|34=3C7AOP!)N9}4JM`TzAu93IELN1y zS}-`C1O@%GD|72Ybu@-VTum1tO@|?8-KLqSxS&C!O|sp38!vkYUn{T@^9c0%BjU=> zmkII8JazG%zCrgU;+`BYzQLm0`#9l#AQ-ACGb9x0qX^ujtyeKlK8kRB3v>5-f^Jge zd<0W~kx<+9yI~op(&4_JCwtDxTr-KL=!+gfD?xuPrjWRZ7u0Rn2jZ#qlY$J=K&rft z*-8mf@?)q8SSOzHBdIxfIizG{@v!Pn(j4qBSe;ZfKc#fFu@DeY1zZnR^<|s|TLV^C zH&R}m2EwuF)V%AK*~PGswCQ-q_o+7qm`7%hxQt@fHk7G-A4Q5dhm^xFAn=BNRc$>`Un?0J(gPBcb} znlRZ+cxg0SmU~$lh{ZB53kN%#v?l+Z!0>=vgB)Vh9?IzT)z#r$_z-mhnisD#2A4Fw z8nPZ-93Lv@4O*d>BeiNAddJI+Q@&nfh}RgO5`T$GC1E0YhHcxYs$1#nv8G(CCQ^y7 z4@Zu&n)Z~{;hAP$`0ISX{Dg@5eJju@ZSGRke1vhbcm}`opRVb@g%@C-;w#SfwwZBu zr^UNG_d72#>ApWIXBy;-VH#o-Thw=jm++IJI=t1_FluLQc+#P{{$rPK?5L0=sL^~a zc!kpzvCIac4$EKlsd)kEZ{Z1UiRjJBx&8%mstu8^b>({2}8G^c4Utf69 zQ$~V zn>=H}a)Go>lVBJ0KqDdc-wKc`tcD$J%WB0tIf54$(0S}R_)(L9op>;?mVbTivl#~? znghEkoW6dIMw|TDF-IoR zJa^#E7H=7o(%j({1IW&%ske@2rZA*a*@$U8Gm7ZrsU|YF~b8cY2FxOe5*zS z2#o4UFJalgEA&Zsref#;*kGnnOSpSiZ5nfJT*-AGa`fqln8D(om~ZUx~)DUvrIel=bp z{7uN3L$JQgtn{XMQs@omOz1^a$xglE*CT*z#}v6lyc*5YQuU2ho6bk@MrYuv-OPtT zzrY`0EkXa=!fWb;w8|*cr+ev9&hZl;JM0yb)vz6GLhU-GNIqP7{A9=(%^dz>aKLL-5-s~GB)}K4xRScqq z3<0(&y>WO%%=&bJ9$nO*m$wOtcqiR=r+{Tdswjk5LDcTc1zCAy9fNo`t(*Qo4qgnG zQ!xHirnyl_mKLUPn^W`lsUdA*Nd1wZ>Ol%lTNvi4iPJvhUbmdPDjYa4TlSTS{u^2h zjnP`ot>vn@OC&^P-!^I1ZP{WUE(SblgA*P3D&w&i4ColLyo*lm`~Wg}YHVfAewSK; zzgHqJrU$A=3n_Gyo74rSLz5&@J_Ui3k-<<`XUo{A$V5+Y-Jya_UOA2o5HGJLPASvZ ztS7@ahQNk)EJc@Q7Dssq-Mk-z-dyJQbtGUfp$MJorA%D3Yd#z29GUF zVM(-o>M2?ex9$8R$d8kj<{R;Y1!F%ejr8_5H0p2jjFZG+%%3Hyj=IP64M~KQGV}j~DY=%@;{Q(| z+48@GHl>K@{%KVG6J!2Amy&2$7D<1D4;)G057how{Z%ji#iwaVH2?i=-~V*_xf+we z|3Ae0ANnv+VXFOEkv!XLKh0D-@NZy!nE4DL1=%KD7$v!OAf0(p3AE0z^S|F9O*u-F z)eB1+_*P<*r3mydI{qK(%YPk@G|dyVxKe3XNEX+cM`hoa8#&f5OmujOaUuI`sde1s zNeOqOUk&<+P%Jeou;N{;Hb%co^<3h@E^vNC8dfLfAsVv7el|~@*4oun*4kj0if>6aXgCVgI{h6 zgI~S-f$gtaja*Y%WUn@$w{ekD&_t<$f3gYbI1oMG>BBy8Bo*G*dAqdP43hsI>*kBq z)$BQOwF-E4po*H#DbqTv^qbHP$gWEcbgcllXL?%AkL#-32v)YFnWjpa%fIPcf3ni3 zAWkR+q{`wX!(@wyEb-TJ7Vq&At6j6$gM}Hi z=dcDb`esCw1-4$08T8eQu8RqZ!>(rc`U#EZ7*6$?>>@a3S5}xgx*wc<4aOkZH7YM~ z;2hVT5PU}2n){Bm3=G&hJ3H02wFz~wls&1I{8yvx15fyPcn%Xy6bF4ez7&YvESY(y zDPC7pSI45q!UX;CVl5)6_&3~te@*inTe@!Ic<O0`S~-8%Y0I^T5YnQnV$ZBYb6w^osRxc5 zig?P;bGLa-ZIJyH3;yMl7ThJ}jg{lrPalm5K#^G=Q$j}#)x4{vt@FHWi!9sqwKPi+~9Q}DTOFMC#*mFp=E-c-);5icU!1_&vI_ zd;CHnm7Q&ue)k376`|bY?V$L8fuphUJBng$L$5-ygHZPh3nX_#GC&wKZK^kkK&@NDm+()-A-!A~;^9LZ0&u}_RGaT5WMQ@r$gRWcl! z38_eWLu{ry5lUyzYF@|pm7K%@6klV^St%F>!&_AP9)ndSDt`a8>!Z|G6{j(RV@Mb9 z3KfB`9{Z@buhUG#uoxHAXuEiHKql;Nb&k5i=b~*2JfMZT;jkTf<`_=u zpP3F2%&ZUJJ_ecVO?JTJ-7QZ;{W7w|FM+{+Y<%dS*y>mD)sIKj4UTXpKUMsWG%kWE9^Q3}AC=^9yYYW3DOujsKq585Q9B8634}Wk ze0sLU$^(mgpL7=8LXfgMF7F$v{^4Oj;fW+59--9_dQ$-Mj0N4DDcDAqK$u@|n0Zra zxJz-dautZ#TBUlQXmtzt%;g)2&PA*Y9m~H9TeB*O_=?qv4e@*VJNT;^8-+DyI9z%X zKPf)_g=Nm$RqIrL>o)qYRyDJ(PJ1ZSUn%$>IC-oiZ{a7TT?gC7={vF2SRVv(u{Q4} zJhnlF8$7$U1f&91R5$!h zqeJRNq~)yNl=7O~f=JC%<7RnbJKX5k;iV9)f$EW?w4?m#OJ*WkwUOUT>XS7!4sOQbXS zV=I25S#6km=8d#u4=FQ@t*n6JzkSX}SgH@L<0O(`F&-T>_PVzf6pz2dnAF1;HI#De z;16Ek3NLJIR8h3_#MtwLIk876iEsG>F8RI5Qvx#)#=1d88LmDwZ|9bl7`q>@tFJ{+ zL^f8jGhMc~va;EEJ-I%Ev3E%}+01jc0~f#X;ciw|Q0EtklPbSc^w!0>O~~{)YxOG4 zq)-uuH7F-4$Vy9xcBKaj8Tp>~d%=7ktSIRw0lje}aJc4J5pnxZ(HTJyC?Rd9w(J;{m+^OA7F&Rrm9V)=d zJ+06kzUMjPiWy#%#-osw8maiX)}fwkU2@&&!9?%vdS>_z%;<5?=;$yb;^PX#Pk{)2 z29W=fJtC^K%~vMm984!ET0RtCf`x1UFMfF7RAr1BtOZ`CZ_9D9JILK0`_v(F($c~b zeQGM1FHBa?kHehPCB+7k_47Q3r6v8?#E2EcA(Ey^<%j`W#h)U< z@x>7J6^aK{jD*xLPhJ_KT>lk7Yy}bHnwz_>E6*o;TU(6EoNySiyG@pw z4TGBCmr5tY?MzP)0nC@E01SilsR@rYr!X;oSu#cQxhnjlua`7-{6~Ri1d~?5r88kX z#L*vl*(l8E^7nt9%W`OlPv^&qc|SDDBfexAN%9Wl5DXWDkPi#rQX`-+DziHE!sEW3 zy^Pq5tt5P4e8oy8Ei2#`qLJrdG(8>id8OLSLCEptZ|Tm5i%~aZ_X7RyCm49L1s2+u zQH1_!8d2QWn6^WATy-WQ_SU;A7(?dwqr_@~Q*R`Cd@}*YtLG~*p5H6uifr$vBo~yR-!W(u zdaizuvfSNv`Hk^0D{aT7Z-m#KH_eIpGHdU7|kRESKgl8U=Lv zV;$**Zz?_%5uNvO_o4it9HN^Dq$;~*3xSh}dix7YT~RGX?)&%+xQq!qUTSFjd4kzs z7)(-c_rH)hOu0!mJTJIRY?F*SxmjuN8hC2LSHnupm`e^1>oTGjY`Jg&gz-h_QE`Z~ zlY~&zb;U9F6;rz#x<_)~t9*)is68_v(}@b1kmu7m85HRkZ`+(=pLBKwO+LL6;f&@O z&$=Q5MvHnZKCym8+E8GW9aR=1F4A&#(rd+~v0ngsJe&K_BaoW5qLR^!8Vj>fLUPnL z@=NW^-%%wu0K3$9tcPi?(h9ztUm`s*D-!fIlTLM5IH=qVX99fZg`p)MNvz?Yr~TR3 znW_D6DbeBFe%v4#fB*$3;!$bla|Q4;k`cg2GB2c}C(0GXnJ;^v5{y%YQx1z@cm8Vg zu2IEoRNA*ct(AYB%VL3@5^~0W@xQ!c31$4|ABBVAVloEURI*_z=q2|>2G^f*2yL7; za?;RCvBO^GORj$!`z0Lgj&S3?yh}_(2Il)_HU`K%UzhU9>x%Pp3{dk8>l|cyMUq9>d-a!5t3=4}2l0yvScR-Sdl4Y(qrcrs1Jh2=yW4hKcE*>1 z_McrP{Bf5XX=-{N#(OuVx+i<7JA zW*p%8u?d2Ly9357C}B6(i$<}rga~pWq}vcI!q`rjbg;SwReedA=x?>AZ?D!bCxki3 zdHcb?0rBkXgp!VVg>QzKfbzI(GJM}fx8M3hqbc(+T>ZG8N%8pG_5=R=>WF7nM~^RF z4RL~y;nJ!NXBzAeX}?(AeAL=_ylFme;`9UH;)aIr`_LN1UHi#I$8CQVS04CHm!tUV z0|1j-9w0G?q3kMfE@bnPnfMCB*c{W)JY^V8`S4R_G^CH1A@<|D*I+*cr8)YN>O8{C ztF0EUNxs`35Uj%;vMWY>v-OV;)8{gxieK54fiQCvh6Mo=-d;1=8cNL;`3!3kMF0l& ztTx}FVbm=ZH%-RM5+V)Ky3wp~j|$)O1WE`?8jd{zLP){2o%`gOw`L(;IxjDu+loQ2 z7u6Yv;ydmPqpjLbk9_?tmXVp=)tEe_S6KcO;86YJ1pP#|-biWS-Bjr7ZXdIl>#oeZ zrI)itb7K2c_Tf=^qDmRtI+7$Ae1nJq`ln*sEhWYQ#p9I*GbUXs`_N#<$~(o`YTiZo zs32f7CgUcC>6f-&En{>u%0Qf^I06j-d=OQ*=SPImT=1d!ju{1Xsbv*wn(qoafj?BR5!v&P;j^DSvb`DavtnfFbI9L-mfFsb+u$*I`h9O%0j-~>fPj(vPb%Zjec z3qJ38D5cD6u|wl z7v;yJ?-er=Te0UoXR@3Mr=xDrsOmLPoT#m49F>Uk2=PtMi$JDcyCXZuFN|bnIq*%l z7}Q#2WCrWUZ(!8XJj>1M=j^r)DhRbQ8B6wC9yw~kAP8tt_HBtMW;i)@kgdAG?0WD_ zS2V^^Iw-r=a3F)7BFP(VCR)*kOQyqoW#fEgSf_bW%;A&7fREZh2R}`HdV{J|+vy0e z5BEiQ)Z@t9zQiEh$WLRyOBc?eI0tcLGKwP7ZZ|a*=;aCDb3kfcLyL&he=BKY zNQjO?Snwv;;$`8rl>R5N&xwa1w3Q!g3H@-^Qr${`+9+3vicjZoz1H_urerkwEjZzu z{K7~J0O(BgNu B9-}(nb_==ZU4a@*Jm{xP!l{hfII%B?U5L6X-$cEUg0h49FfKsP?i#f!O3ky~L>1-sv0pJYq=)TtSlzV>WGGZ_FOnH0Fy$2Ies;ue z@dlkmfC^m*hI%~$3}XbCy7NgAOW~FKoc6d@@h!sZ$fRkX$x@7#cTxWFab}LqoG#hs zXX9c7b%GSclR`xSjDVN8{gf9}&a`hF)2lql*{AZMI5XdrHsxrJ;?%h@p{Rt%PKxR9 z0gB=uz%V_kI(HkeIo!2mYIOv`bS`-4n|YF=%D}~r!e56L>Q?RfcT}cvHD=~*ZgL(%5pe>t*C;0Dk z=cLU+-q4wIsgJ4oGyP$4%}3zxNL5}WJ#$ud_W0f3M?@%8=3jmc zG5*VV2mJ?r`oGry51q5rs%81N&BPQPMHyMyxky#z@=$rnyeF14vGeXhcZ#GOu>hd| z#e;;`*7eUedu3e#S!J(TyVm>l!I2EYvEkB=slebcQ~dYZe@JAMv>8BtK3-&uW}2zG zkgdDnu?B`A1#z_aAb?tYm)`z+LJ;x1?4pPx( zo^t2n*doF-OYlQoW}P)uKz@mRQQtpDB%_%a_s=s1Mwg^t?Gh+uMZE(&Z3l|h-GLDC zSTZ;`>GIT^R_MwMqnHb$l9>hBgeAMK`$*J1AeY`*c5m{d&f@jhpX%1?%<@ulyenEx zs9TMX-Mywfs_@3=a}sQuzKXPfG@M6lAm4W{C4d$F0qr6!J7TRPx_ZV!yW$z6W(VwS1a@NVSP zvg2xaLS+C0g;JWLTEWW#4{(X^X*^*)vyYvhOfz$Y5EQL7KyX7K?=G4w+akq?7P9FS zU(s_+CO?y6u*-_Qg%%EH_BX+7no$%JTnmwYbrV3NxqMwgG19U3cL4} zK6_1Pel|@sWqAeHtE-0bE3ajTb6AC$?eEMIrjeV!+C5yDGR!o&)#?{o9HYrL@x<|Eu?bDzcP+2)BQMGygO)Csu-{9wEWsRIpMN-!zq+5P5$kkSgQ;wRKrM&&5dqo ze_{4L$;q_3wX6n(zF!X1rrk!5uKeik2N^tt|6TDe$&Jr!mHe%vm*HvRp0c30R&`^; zJ*tCo!<7b;+D55o?dLy^gNyGGwCF?kK}Zk#{VSKC-20sBt|y(=t3bj=-wo$S>N`X5 z@x@9mO_^E&ed z9D^WcWzfj{{4dl}EOCuZ@Oa zSgk(Gzoy%FE3QI#k8zp*RkAj=^tA{;*$rEdEx&qLd%`H8=^@kNN@8eSi^1j#a9IXe z1j4uGXnCN{BCz48-a9~C-RmWV(&TM{AMOu!>ujH>EXa!pRRGeEO&+EZLc=o=sT+0y zC=bI?%gTp0@uQs}g?^@XyGjuz93AjXVi!=-<^apb#cZ4oUE`A}8(rTdk%d6M87lHgUR|-U)I~Jg zWKg_fP~zXPbZM=h(7wRThfS$S%TGMm#0f=4pKJJxLzYz7OOWOUdfKJU7q8H)`eLZiZ$ zzxBC@$8{dx+(%Kb-?)=uJ`8wJW@K;`I5H2bZ+p)!Lx!2kn{BN-pXyex!)p!Mho&f| zHzfKuIKRxnN8v2HzI==J!LYc8eU)aCU5|Cr;r_jPfk3u$#!NK1)TmWuVsVzL zdUY>IIAQfk*S9HQ)%MJF6s1J=;gSz2dMsnb^3|TGo9vnO!au|8_B_msZ7hjHu8 z8O~@vsBZ&g-H084qX&qS9a~YkwsK|4^O`E(<8ez3dHBul%nrG|bMU77oi%-FwF`n? z7K5nOA(Z!|*`0FP-b+dh{XJsX2M@!8#`W@{pzS&A2>)i|i_9Q@MdTI&S-nWI z6r&{s_3#_)bKm0q35(oulF>!NX}$+4+N4aI-rKo-`x7pCIcl+8Lsw%HZ7`}5_OKYZ7<9KK%vx#H!H#lya;7V^My7QJ zOEDj&dWLqL4uyupiFFLeFYd{IOObf*=ppT9Q>zxC$Mxn!)_zhu0qR_YV4{t8#-Cm_ zIdks%f$f`iU$MlSEHx*;K7QoP3#y?7e_Vix;{lt&-=JeCsu;(*Eisxqg^cNV)2+g{ z9O@R4LI&DslWzF*IPV9$zr(I3byxT7kDpHnb{t9u`y{f8HnE!B367SWUAFipiiEDF z9$FpvO?>9x;4$bAPL0%Wv}+$q?P615BV^zTV`MCtGgK{(+$eU8b^eiboOn2LgEgIp zQtSJB0&3oGv5A!=KkMJt4LaIg-Cl}U6NFN?9^FA*mvMTFMPTAtKMFGaoJWpCyC9C^ zA@Ta%7fR;y!0H$4{K^ZD@sf_&v` zNo#Oz%ESCU7`ivNBjvn)$M8|p{jleUR4r;wOMyo7{=nFp;B zlnyZg>VT);4tJ@8HzS9cUU)2NY}0ssyvg!0QQ;V52?;+Co%_@FE<({aSY=Z!2rmo% z6(a!QYOUrv!#-B6yqcvZ?gci?hGbP^jYNyD)5SjqU+kVC3bA>BI9>LBvc$$?Ym>8B zRtnG;U;Gy-#KhIK>p#GYO5^3o{|GExc!WVC4UqqBmODySQSC%NZg{ZE4?58J7=(m= zb@}U8Zc=jcUcP(?3VXI6?AR#Pwz^X*M(C)-R6Fd|XHlP8*3_&aFo> z3OjKwBI9&+Dsy=_T-cbUSpL%|Ntv{HiN6*YSL0t8m0nZ^uvn?bkn?y|yjPzCV-5XV z=VAW!FN~{bV~5-MyBGbsAyGvvP4aK0GFShfYT;jq|2jiD1%o-%EB`E5&u`B{h-ZO_ z&|B4M!Gp{lVeCIuB#Y>y{vH1Rn-;{n&HUvw3qPL-t3tPTV})LV$@IE-VziO3j=OxL z{$j22?!xij^sHmtu`&lx8kI`-GGXnyza3De=vz%Od<|_oWnXTwY+Q3b&zti)-wdM6 z+zhYpJE-5;>niZ~IZKeOXq;H-*-VrRT#+T*xKHq1gSG!2s^lL}&md-%R`o1fYz_@m>8U|P@cVUvw+^YZJNl38l2#W2gM1(W`S;_KOU=*ma*1<*F4kyOf8e8b#_(=>$hr0HULX7YPK|i2ma0 zVtTL}yQKLe03TK52c5yLe%hQ8Kc7*$Baf%1oZ+;iH*&uo#+ST?g>)_1d;*Y*#F;Ej zjkY~!5I$5EhCa{5nX1fi!ypPhH7>-b`imrwBYx|ID6T=7@<(v@lGu;;)s8NC)ckz< z9Q+^3i390EUd3ju=FBDh-d|T4$vPzLm5p(i|hTY7>K#pAd#5uY7ZJPDhIQ%EFGUY_a<+un zfa@;})_1~SKOJ{N1R|%dNx|aV`_Ej8=B^;R@*THE4y&yGSL=DfumYN8!wy3F7tra5 zko0cs*gl9wwf@p)SlVhRI@kG&Gow~9CJXW zCoCHZ{1xuXFZtIfEgjkopK4p5%5PBnsCC2ewQFZ;KGpevsoC<*#Z`+-t$nmEvGJ&+ zWg>SIq!*wWdy-^Cp&?~pFk!ODw+7!$;DLe=bDM_Wq{q=ANa3P4Te{A*){;}d#RjEJ zx>5fi4G)Z6Mw7qbtT>zX-dJ3akVr}XY`)H+Im71Eqk|Dw*Rsx9aiuxA2pV2}8)q87 z)Q!qzWY>$t?#4c~)wxtrWYGG7uWU0(R%nUhTb!>X5gm89jjqQq^>WGMn+T(kDwEa3 zi}3s4E8x%Z3xt!Jv+6-+~A#sW)Ny&9*R}B4E3*7N)S-9u@VQ%lQ{OP{o zXl22Eale_SNv%kXEeDGfNyxaRZ2)aj{A6}4?qy_#&3D-KOQzzL8R!TJU%PK_?|`(+ z7hB*=t`T`*kzh@z-na_U*vrf7&7@Sc_dW^2ElUMmIji2uQTMWSS&9*EOoZQfjvSxb zW~)<498Wd#Mu7rUcR^h8A75;^l0nUJY1Lilh6_KORnW~l?`vz1jqe~@&CB7RoP4;i>37Ul ze_l97Ql(>$8nl;da0r!dhLFr}-XC`nuOS^&U>@(PHRd?34YN3nRyZw-@$09-;$1wiY;U@`gTrk{vSiKDW^=>zXBT^9CTM35r0< z1bFdk*j;`yPCuLtoMkX&w%3CjHf+eOxu-R`h|~f`ui1Rd_E^2-_%>Eqjzy-&WzYQ7 zC)#Ehjn`=s>-6RcVCimB9UqIa3SCFx>iFJ8C ze%1s+*H`nM0pFQux{`!IOTP>BJy$1_zFoxPQY8k~^XaplOAG-ba_|t>i>tpcM!etd zjb|hGhhXd1sZWp&BGDf}2ISWLE|VEb4hSs_9tbESJ#%{HWaQ1?Q{Zz9t3QyT91g*yCMDW>np?ctOv3*}`Z+1SXH9hlRib!wkr4&kD?7U7q zWALn3K2{p>9`c5xfgmg-Yme$G{PqcgravkgE?p-0KFHs2W;A?*P9B*dSg+6*3c1m` zfpY~~rXti<(&_}|oFMihZX42@UqB1T^AI7o=j53yB`;YJ^-Co3gY_hDn1Ow2-GI|thx z@ek3_&%iy=a+^-&Pt^j1EP;I#fz3e$#V(rxcQ%GKvMfG6|c zr#JntlWG6y!T$l0eSH0#gS9L9|02_r{)54Q1R&G#dm6sz~cRzNb!%amc< z0}sZN2W zU{IlPlwkNwlBF490JZfpFy@*Rq?FS8+k^}3bG6cKMiYy8Zz>XQ8d%Cy4s8;vE{d+6 zDYYStJgf78XGx>^VGT`gzM8DOijcawTG|kB&MS&JLf~yh3e4}muJ<}T%HpmB8%l_0 zoqVL)*dXI8E+Gfx+gpqb0O^QZ8H$Z~92>1Ocg?F9=zJ)bQa|mkO(bSZt5ug3%cq;* zCa1ehvNB{H>?l@>5v-IZ$oB6fg;q_D)beT%JC%#k40hQ!j*+cp7nv6;A;iW;8B^So ziVL0+N&+X|ArOz}h67~g;8|Vm=`bE0y>U0=po!iE8 zPwTZ+X0hZfc;v$zr4$)P3Y$tL3TTvknA&{q3yTJDr_V4(pB2G!Dr(Qt#{8JiWlwG= z@^_)Ww}AL#mDivEGl1CLXxs?}Te6iRveMo!tNV*1A+zmq-Y>adj!kEXaC&=v#lhi> zRMv9r`leY&t!2{QJ{g6~H3V^K3%g8}^XlsfQ*kw@P$49RVltCaoi6<3O9!bo`v!um z)Cyo@$cF@m?`+&D+JOCTs5|PoZV6Tt?g6goNf)^~@;rR}PoGLJflo3!V5#Z&UqLi_ z@6om8GzBWMkKf$Un#<<`D$4cKS5~FV;rq%erAAgs3S?wkg$lZxL0VpVd!dzXP#%Po<2=-hWl4cJ8x_ zr_sb5IS{74U)BC%aeRh*Hz9aw@$;!<^Cmc^LPkn?*VWy+DF-LMON{OnQB-fZc1zcX z`*feeWnKxOxvfPtorQ_PM_a#g9uH;{K!a`N(7FscmCsh0c|@al{!D#GY|DNqF!KGX zyEUqVrb6`&pmAu=M73T|dtJk7q`Yq=fO-OJ= zS;QLkl318qU5e)T8Wq@zn9qUk2^Tz<52G`TTgz*eM~1U|6v^Z7-92JmrLv>e4(&H9 zR`RuM678Oodju}E=UFj{3^FfN#Fmi9kYKo{=YxM*XYnKx7_jFPh>zmI+ zX)_x=;+d5fk-f5{L&J(dO4>_{)G+)9&HI~b{({$%I(TxhWLY7iDLQ`?g#Bm-9PqlxPk*ofk?jU1x8uBQn}mQ^QZwx z^w5yrlWX;lZVIgli_YMSJzn)h>|h|1?YlNU4>{>d{;z_D?|pi7+>Y$u*;uO!zuSc- zGh);3&wE<9dw+(lZYGw&o|#Z#x@qzy6$97@tJT%jtgbv>%n93TAc|eHolLrmUMekr zYX(}?jhpzaFDe!HwE_AofFpKTV zH_RI~wMkP8aS_kSF2FEKK=>yC^P=nR*){(J(ejl<t19S^^7HE+wk_FPy@x#Nl%&88-Ri#ul z)C+72Bvc%SCZ#c-16Q8;yyaEL<;3hHjye?fEke*w20LmZ+41xnUF~6c-Bv^B#T-T0 zF1}+9i)=C}P3qGXrXY2Lk>MK=nHaS3gvBHUQgKILiVVf%_x-Smh^QtVmt!H=qU5gP zec6k0E#6HwxA}Rzs^fXwA-08)H$QkciZ!MzH7dmNl+zt793^G8dC#x7=XvLun%vjb zp+iEYy>57ysP;lIGqUrmRn}q=57qpxF;Iszk{_U7zMU5)d3{xqn9WkeQJPgGjQ6c5 za>!jXOYXajw?}6KGl_lGh>LIuVwF0X#hb&DLjHIH_O=`wKyS8LO&Nz1>73gNED~|# z`H&I8#OBy}mVF)HhfSI2#h9b0r3DJ0(mz5hKia?{K*Exa&Wl%l^_RBk#A_Ay@^&eq z`*};=s99yzr!VkyaSoe+>n?%;4Jx_Sl#(wh!jzf<*2L6a_A_c?WKg~oh0}Xt>qtns zbbv11zLJt0!gBQs=S~};hRE3gkitAH+V8SL0~$2|Er_udem?R%ihoXcqqv_6GheJS z_y88dYUxG%bpOC1woVBs8vTKV+T_h%Tj#Fz?(G98+@toCM zboPhQ0-Oau0FB9HExtqYCl_|yLHW|MufP{!LF-@S8EM`flCA!$1vp2YKnlDa>WpWW zrRQY%1jo{iWB*Pt(Qgx%3;qz_U&6Z}mg;)dPc(~X{;TF*+G72?f|}x5mW@qy2vJ4p z_$Z~=um#m=y&k%&UcRWSat4okti<~`t`GRl`g94e>qca=gI0dbu~C5z#aE~dW=m&F zFwJWgh;)}gv8zdV*MJu!303E0nT*qxbMXoVVERe0)$ugUk;JArYx>!te;c}AU>Aph zR4ktJ9fd4BM5xhjsFfBmKMKNEJf{F`nC&_S)M`Ioh~JFXhs8upa_>x$3T3vgoy@v& z_QdBVezf^ee*L?s(m{=WWpRded|aO5gI-~yVVH2TO%wg*4B~cJ@@d0z0ndl$V-}I@ z2AyVbv1<`pqVoEDeTCp*Gx4R0G+*g=4atvr*$w)RIjSOq8Xihts~V#SJ+9U!3U>Ni zi>(}>#qtYtnx##UE5u6KIk#w#I22PA#Ry4E-%cZ0 zCwrs0lx)~OX_f9i1v5{a@o7g|XJ&a2T2K~Vdu#;N+=ty?6Dh4*wop#d?H!X8eD7c| zPBM6QA719KiKLCxo)x%xJId2YocyYL&!o95otcZpF$=??_4htg;=cL~l*2QAzWbz@ z1v!^2ywN3MYqu|LZWb8R^bFU0Ogoyxk_rGhg<|pyEuoC#tRyW~4PAzLTRk_s_k*%j z{#o&!=3Gwb1au7Ze)5!*_49Z<*;OfW$A0nv?ag+syd!djaK@KRN97B_%L3NDv-N}~ ztU}D+JzeKAzStJ-%lH+IduhmfnTWbpX&+|Ae>=YJI62muuZT$l0wLqkD)q9QT7%LS zs&H03Je$I^+@i8k4k5z z&d?-Ru5B#*yfV7FON4oA$?s8_zZ>M_)L1=^$g{0=og@tmz^EY z2xk_`QDtw^+x^X171zca-akG!+`}`w-%T~+|8?l7`DH3-ad9RR7qQc>VEM=hgi=!9 zW;9|ZN&TFd%CmtzC!dI!G<|z&7F6VO^eZtT!?k-Z3c}oILvkyK^}N3t%}X*SVAaXZ zK}JR{tf;Wlw^+tFLm3V1%Gmu)(w<2&_eFlL7Ee7a+WQ-XvqNS|oU_w)0zvooY;qKr z!637_?fFw11aW9xeAXPBT?G$C@ZV*PSd87}F!vuXMKBp2Su?i~XEB$qLk9Q|Y2O zEMSa`dhL=k?Rv-qS}8z%L?i@>wQb0qswbMn<=dq^e-;9yI52bw`2AB`S_~RDgdRM? ze-NHD=ANyvZh3h>W+u%%0w7;vaNgsjUd+}xOrQ8aczf%xw%TrekP5W4MG6H9lv3Q? zio08J30mA8inoR04#C|?kRrjQKyeQQmr`5;g%DhR;eFrl%$YOa`JI{Tnwh`2a*@5B z=h=H_t##ka3~24l|0(L^6z=!oUh;EYp1?bNov!ljj-9$jN0?2`wb@gR!dB)VW}p!F zm9$JOE5SvTemvO|sgmkma+++br(hpLz0GpTf4*ey^WhaS`cSO<+rPo?eZLdE6{~+$ zkN$s=Nc7+2+Wg<32e`nq1n;Pc@>>p*R@?1pco3NkQhfJOX|8#KC6C`m3%Nb^G+q5&qulA&Ltqm)zcnUQ%u zNH)~N92oiR4&hOd^$NKM?iF_LEcDHK31D>Y5DV5E)IG&bq*QLO+SY19M3Zr{yMDNZ z2XjRll5vKI_ecmZGUB-!hq|X%CXFW6&}!u;{M(vJ0hoMVT#UX>0N794BW80V28 zbei`aBr3L;15{AD+T$SMi~9o4#t%>KQU}Mx%7|a^ zor9ee5-&?H*^C#ZyyYw=ESMDL0&1El#ymh)-ui4+$K$S}l;@CV1E+6zv@+A#Uv9S+ zOBm*DP6cIzRRE#j5^*{GqM8Pc1P|(kZS5@g-s@>B#jNbNngNXQ)3!Dh`#>?aXETcQ z23z*e>rGL3ux&(rqr9Sr5&Gu2QD6k;-BN?1^{50*%70g=_L%6+T>hyhQ1e^&ZWHU; z?SSZ{SI$dL7ka&2#+2ey&k0+!2X=dLt}M!7uKYDHY!|uzgtDWd6V(c~Bnckwxc~-z zxQ~}N4cj9WEU_)O+5M%XUF?JIuTlw5Q0Cv67Y9~J)#c|IrrRza^LfcDYg8!FkW0Us zBK;K=AqgF6qL$MZs!qz*oYUZ!- z<{w~^Y2z`De~XLiRRBiOf)JImG8Ubgao1NZ3g#EBCmS%2UU&WwXMe~E&zXW75U+Dr z#3n{@UiXW6Z$hyc;mqI?bU~;+q0fkh29_UOXXLGIReQ2r5v&=0u+(N}9?7dg!y^rv zm%>Xhjn5;Rl8J4Oji0i5YCV!5rlgg&J`88}a<~_{I z0pYR*%iDdk}e}U%a3GCUvXc<6D(FA1JmCVI4&J(i#+1BkZ#GLgJmBSO4CZl#4G4YftZzwSA^25b05zUX{5=CEMF~|WgHHBo-kQ_;$O(rm^~K%Qs*DNm7jMrGVPm`^ zc?r+A10@Ad%QwQ7y~1?!$zHB{&T}^za*doNv>JHW{mcrv*?fba#~ec@5rz}@sO+;- z_z5`CPfpjbSYWm4hk4G2)Hb8Raz_T8P2B^-_Es5NPGTuu4>=v}be|2W`i*x;LIzN~ z^7vD5}pT4wKuDf*v zFptObVUv>?({jH7Zp(#V2jpD(uaG2W1m>v7wNaXwmm78Ij}N!nb~cITr(5c=xIEZ^ zXcx&RD$Bx1)D)-yP!eg~u2liDFOv`P6aqAvxQ?=xcVFqlpx`sIJAoIC8R&q=wh z>W&5`2e@n`Ga+4>?BUQcct-cND!(}VgW5qMm?UuT;F*e7)-x1(?XV(UbT?R#e$t%u zo`gbHs*W;Jj-9d}cPi;C&2XXo0oM1Po?`7mS*(Mh<9w0tN8=b= za}4ogz?dHK!_^n^LG20u!mFl#<5f=LJ=_o4CT*_NTdw8s({@$cCrVDQ<9e|h4hwvY zpBI2$`x-_nHceGl#n*r8&^Za$dRR5;wQsqn^RUT?r_a)SW0zOX7<6E@C+O;En=|n# zXUfIl399>m@I&~)bK~=`glb`QjjJJ~>!@2lbI8_qqAna*{^q@3(xmLGhP59zl#n<| zIRIr9S%iY>-S;Hy(FAMxr&*6V96<;H(#0><2XEcr*BBIoS&V%$G*E*bLGp*1Iz)vL zFL(CblI3T-Ipje|TR9xZ_%KGVdF-!2{ZVCb)Wb%Ef(k==|y229w*@mX2&~WDBZz~(;3noD#xS^v@#ld__8OvxAIe<+>+Jz zV%{ryvvh%TAkd+WGrr38#j@u(Lm5wRZ=9<>1-J(HME<$?_j*a$FF`U-WFC_e;c~YJ z;nQ433Al!zEUjOVIS4o7I7za|GEoNO;-dHmIeN-`T-B%ByD;z?r7dNV*~a*|AMu-X zx}a`^xvuEo`ky}r#BbB7m>kdB{QekY)VV$VYAEoCt1yQPKYkj*qCRJ@a>@jALwOmw zH*}N5yR_=={^Il2MR*D}z-fCjbbnc7qjdF-zgb63Geu1aPUj7qbZQ=uSVRlyNc4)udlY&daH~KP)nFADb zV(BllLI5*NZ7!Il37gBQ+zW_A9cPRO&mun1E}hcoD1jVvKA9ExS5!b}T)o7mSeTJW zIs9I@^ySo6Xwe+vqVo~?H{hoRYSrWALCXmI$QaO?<$E3y5V^LkO#Cu6q>tnSt7V<<{hyE>H*Y%sw9htOsky8F^fa&-JHVo-LT9=nA;)!avQF~) zl-+XT#hFmn`p-!lh!XG564OaUwS&rqbqB5N>=0~AF4PgQ4E=`7If>cRO7-gEvvhMy z3)1;~jX;fkk4ViSjYG(tMry_C)z}2N2n$Vh>CfeW8ZI{TU`=^c;t@uY^~w-eY9XNA z#F;8g-_7`szszL6^=X6Wz=lYHO51qolwaWi5sA$xbJD55EaJz%OJh>;7VL7(J*4Ho z?qxmuL9g~TVy&M)`^4AZo?<&E)kwhV3$x$Z)<#$&>*x_a*V0|$*%KP`Vzja!4*bUy zMig-ot_%$oFIb=UBLO^Yx%^Qnzr0u`!)Toun$pMk<0BLRjgzxsOU zGt+W>nm5sj$CBM_FVwf#PiKTfrtSK2k~rBXU7i}-!qw91@#2Z5ykc!4N!b%D=wApkGW|pwx8oqvqKZAGWRjET@Tr2`%@Vdf%@TX>tN>J&jp3xOo_Trd zRaEh#cr{<8k{@3p-O2S23q8vQ#$$OiEKv4AAQ2d~nGx5tR1hB4Tm?zOj+V36Z2K_O z*vaC8F*QP6kY+JbxFd0XogxV`&p9x&U2Zr*na@b%3!eh;1SN|<%WtxEUi-+Jn`Q+T zz)txjc=wRV3f6EXmPB}CBy7`g8rH1EG9qG!iU1Oe%GD0*tTX0V+-LD?q?AM55hKiD zhRjFbH9QK=geq^tJ&3qc{QVHb z;VQsQ$w`tpE7zZPc)5MN*1Mwfcd8$%Ok+Kl9)*rBO#cH?#y>^{fd9_LMR79p|L??< z|C`Y6|1;r#6=BG+N_nj?bo$xF*h!)=@sIbzjee0=4E3 zs0z##IIb6z@yLS1BqS7J1ycEH$oGwk*dmuN!^xI){u4mtR49HGH<}s~lKfLzQOZK91HSmLO3he!!q(p@z$tn|< zmo3W_(&}aEHFXpf3l;Qze2bbCgo2AiG}%@`gkCpcb-Nq*ic=oFKQ9`myzz`Gzi=GV)h;Im+C7*avhS?fmHwnC zHDF!CE29qp#`skKnVoH28Xl~HXT0MTk1J4t)z*ZRFC=C1Jk#7Bf0z_uikRkqM^lmW zi!?mSkc3_79TC11*F|V^a~(U~NS}|VpfVROIuhgkn#q5}RFD6Psk;4=`>(j~RIB?0 z;wSMG(;Pamy{#zf@ub*AiaTO!n<89flD4RzTiq23X(9rl=Sou&6S)_O3k}Vr^%LhC znU+O*DEN3eQyRr@x+mlff({ZbonA2;u)+LCpcy2tRA0?L>Y8t()1(v}m{B8%ACf>y z77#+3+VWb|_TeRXlX6)JQ{Sa7fAUO)oglU3v&<4lZ52LV=_h^SUFdr#rCnH5q@u4L zJ(UO2OW)Hk)QYqh0ffY*$^D!7X%~P8qP_s56<$(!YzHmZIgskZtF=vP#NK{tCehz4 z-IS3aXrpEk9u$`gBC`740wJx=G9tPr`R_pq_x%lnoL2(5_DVN zvMGk0Pp(DZQ;bZbI5=rc{YjSaJ*7gDSUI-6UHWs9P1X!Au-EL|%Q&$!N3;J|>-wO9X;-4sA-;_^!#>kT$(hA~H*CBBEi67jJr+r2Ga?E*_XRx0TQo_@QcPXdm`}(fmOteF>8nq2?0N$oivWQlm;gY{X`v-H^oO#(c(V zY(&MA_JT4)z?@kTxJu+PMBMPE@Q@PA;~htvom-0Lluv$f9P?BwSdD}&_v{Y82AlEbPd$;J4X3%^SHY+#zAx1f!--VWRFYs{c0ll}602G~ z2~^KAfDF0sKF0-$2#$|!w$6_mE8UjmCW#Q0wTMY#70}t#4v-(zc0BeE-rV@)@UljS z{8iV(Fn28l!+Cc4{9FpmZ>_ODm|$;~Wp5aAP;KcVp0G@NQXlNI;i#9PNpzz2;@ci| z?`_b4)V~9Q8l?$59+;?B3alcU6>6~}bpAS*N#!7$m32)<`)!LvnjV{)fIZH!cH zGw;S}YD+gIV%WBpwlKYq<|gA$3KEH3;t^33536G|GXXLIlpzXAe6bZGiDU*Zw$JL~ zGh*yo=+)D%M!sS8`$&rw@|W%FXKgNKCs0P;Hl!}cUF2h+z=~NfDmuu`O^s^D))*ji z*ox>i%xiLLkx`PM!$^fU^!lNv*iw? zM4A$sQuii5m&1lo_8ox9S3|??M%^i1pjCVQpwa$xSES~*f|^$CRW`L51|6qOL$Psb z_FGiRMb8NhWYS&sdeU9`&Cv@@Peh}isj5hP9_BBDNlY&1Z~u3E^2#;%9amW5iqGgE?o%r{k`rQweb=k! z0!$18;P`or4!pzx-6u|Z=Zhf^Z1$b6UV+Xa?7)cO9N=}Q&0uvs7MI{7f`U{L?(j#_ZV@vMg5-`a8D3_KK;$?Ei)Rmj1$iDk$vdwEthRUoT4m`T370bz!%g zd>s>qeN2T#wVM|*-?8@(fP9{!6m+dyYj}s6yV3v)b~^zwn<;wYa%0kDXN%ee@>efM zA@W1AmzO`e#7_dlCma(#Pn|$QdPo(pm?p{)?zk@+@>OktInCvVn(SAgUyC#U)g9(D)GkpDOEFxK@Sco>%= zQh+<|)sD#0mm~Mt&O0I?(q9-Du6J*;I+D$+OqN z^wbm^StBDmf@fMhe2Ny4JOYeA3l;Ego@;39QhE4S4BSY?C(r57*(FF;>ig`D<5a7_ zN+5$BqD}`l?d3;j5T>I$*qS4AV1T z)Rd+xb#-dHL=wooa&x!PLi&zx^tv4p=uGb_r$)*13Ic7XKent(df;qPb@&?YT`&uQ zdcST~DIemtq{<sBoGN8dTRF9PDh0>9cyw(^|ueO1-Rc)EhPC&(RsS7h$|wOPZBEf5ur*t@Y*d^~oIG zgpd-a18l0D`o7e|ZU0Q|)~&tD7wq9!0kT?aS_c+ikjc-jFx~E;g{4&p?2=ztM(&?F zCFV)8#r6|*oO>feJC>L_&Yiw~{=VxiyB^qj6M1|w7J4W(S{r$pde)*63O>oR0Ib6G zzt(0P=*?FI)U8!9UWZ^-nyzXFip(rMH4J}I0x>-%0$-g70`9!VXWIkLIu7oFe!w>c zjt}97uN&MSe238pRV}5?KqeweG1MyJPPsMDxK5{7kF$1#{Wzmek%(d5pIqW7n>I1F&jG8v+ZgrZD z)^Ly&Y-sg_SOh+R7^80aj)ktv;4@-F7Tilj#7b$)*ftSz85~Ww{e8*lI@Oq>;^0{F zz!pA(ZmQ1knWdBsIMI%-W)Z55F&0D`?Gyf|$SP>NKG?9P*jx8tS3?H8|67dgd5l0N zpIPW6p%4~`U*1xf^o_=xHPVfzsK1R7tdn9-LBpcmBu*4^=(>-Ac68b7CmC9H>m%A_ zCc4n&^9{9`XtH|0LPy!XZ;|_NmU<2EWCB{d{Z<@f9(>-@8*`IE*0cIQ&$p%FH0Jhe zcaU|CB&wGDgv>5zu5xj-x#IsK<4g+N1bhz$=(~D&!Ty9Id}txblQu3{{ftdz8?qjt{p=&RoXe506kT|TW`fvl;%s)?QsMVI z|8A(&K%Ttpui-e`HWru%R3VGG(&%JtFmlK#dca*{b~{L5BlwORnD`q69NvVv12c|- ztk*v1Ra6qvui(np*rqe^VW#QB8EUc_Mo{=;UP_rg}&`vk- z7u*~l=mg3TvfjPZO{6y^XCkON@PIcFExJyvbnGR^_J5(;n&*qDbeQ5t&8k2{tHdE? zjrwR7Mis>xcsVI-_-J;xJ`ipfbnp-*Pz;G;Q0~8lR*r7^>i#coOZ(zH`Zj?iVKo6e zHyIjQZpa}0{XgD$2CLow72ALM*A<^ z`6zL1Yfg;zQvdB*Jcjm+zLg?WzIU6LbM@DipRy0;XtSx!y{}}~;evrW+K%Uca$BCF z1m!*tR!JRxx#u=bkn@#P zI9JWx##sNd4rk38)ICs;%U34mtq`M?FO~-s$)kdSIcU$RaH%^`#k1=h_ z`38t@H8yypHqs=!vAwg|q1&sHm7-yGS#yh7h;$4*=ZaJT}=1})L(M-dgQY{ zj_O|xB~R|Ag7H~g4!i%sk-$3i@Lw*CA|p>MHco3I_P*TV=QlXZ4qRSb4c%DPHyi0& z*y7@xIi6<`HQ)2xo97TF6J#|5-#RwztuWp!tDm&I<`Ru8v9JUlntOVx$OQ3OmE_cm zHgt2G;-OX2-9M(5z~3thANyEn4Op)-u+lI6I)lf~q>@=>mM5=lDm0C8Y{cLt6Kk%g z*GF$jQj-%)4tX6ikMB|;z^Oz|TccPqQ6>*%JA%_~bbihs??5u7mk>3^+@(vf&h5{~ z&|g+U+oQQSN@3~5@u9KgM5I{UCL+QtRJ`wOfkQaDeLU}{+GciI^$;xqi?=buDmn2s z`1-MYg{@6`_*ytKp;Jw1#H1zwi`4m}&3Rg}nABhAf2^J1JH*8P=gyIcIyTzZBhiD_ z8vXXL7|ijCuWk#s71Os!Rd8-J`Y5A6+ojL?ccJo?iN&ZJ@92w;_ndaw3kLI`ta=!f zYcp`y<2Lj$TBC?VBfI{^=2+wz-`Lz%vq*FnC2yT#{Y}4U;?ETUj{Zv1M{@o7@?tAo z9dDRA>ld#}AJpM3k^4JQ5hrR`6|lvo3YSRVSvp+Xxn7>w5DR+*bU!W~A(SY%-311+ zbWHxXiW=z;{u)}qrd-PT^5ZB*&^!9zjf#nGT5chSyDJ`n=0R_&0FUz1S@}CMhA`J9K2YoiqlU zYYUIpEu5qkUZ(L?3_h_0yr};+9qak&K%HHR&&M9o#Xm`hGdQuku9y``oQA3_yT}xA z)5Lxs7}wm+^fXjNA)Ds*SkSmE(;^Mrp=BJ$a{oxXuNeAj%AaN9e5BXAyhGtY=& zg=ld*7@X75T31&;26=EZ*#XCDe%?sP{U%j(Pu9L@@H*b{N<#l$5!UB(pV}Q6x2ModoJS!1p&a5xek@2$)_hI1>(7|)fR!Nx_QgcTL+iR zoB2VHM}lv>>H^-oA-7m=$BZ7qkJkEb1~0Z#?-uPLXkf|QvHrAv=iTO;Nv^%~g9pDZ zt5+Edn3z6x=YwYFK4V^4ueAIDY~BUjCCg=WP&KTaezw0mV&m&DeW*(*=U8T-Cd5Xt ze|E-?uG*?@1f_jt+4l8)b1gTGBPUajW?`20mHzmT42G>jU3&TX$6X&en8;N03X;qt z(eXbjv$GP?EabUh^Uqn{{)6zcnQ(oU~Pp-&1UjLizqoE-GfK*H*t_#8m%`aP#iQ`r>#&!$Ul5s+w3~J2 z{;WJUzZ!G70!+@UjYNR1zW!k+!48TZ|198h{)uOHr?qo#A{6ez!l`_7q>nzCmt7lr zvpc&Vg%!QWGG$l9!aQssS1)uE`nAbM@cPubBAA#shV&r4`tFCRmyNV0(_$91 z?X6quY1>o)lPvts>A~lU$}RTG-;r$d6uxIGfq|lzR0M11`~7M&NY%&)%W za0d=Aj~_1rnX2j?rx?!93a3COO(WklXw5@rqJworiAt!gBv#Iom}nYaHMIqIbR-{L z58ANWRO-~lPx~cBRm2f?#o6I-b$Usd*GN8{!rc*&mQQoeu+tcJpTr!1`D6DP^bzpA zUshhXN6SgVM5NXH<~JpRCa_w7Zx*?`dcwO*8&+FCv~=J-o^)olaZf#P-D4NWNq@-mNmIXFux@KK`N~7ij%B#%N-<_v_&VZSKv}S_WyLmO%XxUZzlQ4tI>TRYTE4T@l z#NqWFQ8NX69;S;_w;qm#*-4YwJ!_Q~U3&uirCC3#0kko3-*(_J&`#e9o@TMFQwN8I zRFR^KJ5FDBdYYbwVOpePs1DU(T5hd}Wj6OBo;kqOR0b{(6?dSCrhM8W$FQb+-^$u| z&8@AJ3uQ5mKBqVV6RYtow?hI%S$jJ;-^hr`cKnQfZqKGhWv7*m?$o(qD9@5&&1a^i zLGsRaxF(2GREN^^L=EJYj$krH+OH;Y`SV+*srO}Bq^=~dt#)anJrBrN>2gi~{He>V zP80!DISYY+TTQ>VW?fzK=QGtr4c?}#VIA7_!`N*1*%PN9h#`fnG@0_xmVK*QPoRp& zsLt%Ci+OqQmaiB7$OW&vbDfQrvCO!`n~ANBijH$H^@|_Zfipmy-3Em{X{{P*d|ct$ zv{zd=lUPPXZV5G78f-OH9*~-(v5XM?4y&!u^k1c0eCVjW*h<@{ZGjl648^p>K}Up?+6+r1m(dCrUk=xdi-p8if$fKB@0w17IdxV;|F)gY*bp}{ z9&>H|VN6(`b7d*f?X6Ud4JT+1W1uOK_VfIOMd#k+mQ+e5)}2ABh_||>GQE&*mlQ0K z<$w$ni8GR&JPB(`x&1S*HImKI7dwE;gtkpfcAG3HCbc^+C>jdb#$DIvQyJWu;|MHT zM*3l!eE14l(*@tfSbxCx7yVSxZ7L0pBO84l0-rV-mD=O!e5s zEac97@~fS*F+4i^la3mF=3U;2yOK*I_;{x3X{eiwAQGp-gCmrWQ?w42IkA~jbz2Zr z86ZS!MpbLLl=up*{%iX%F&0xkiCjU|jPxTZa6%R)o}{?D(We&rD$%=QzxCQ2r}xEf z2;Q)blQit=5rrCq`pE0TeF+I~`JL|z&1+xH=pqw_>8PnjzD#OM(v&7yE{v_dh^^(0 zR#B2?$guY=x05I_`|3(oQJPFgZR~10L%%^|>}g(ekWSm5{(5iwiQDiNXJ_3uL3+@M zZFe$XDR6^^Sb>>GM-q&XphC!zp0d!KLhLm{cL#SSISF6*B8_vvA!31}>zMq$3I)3i z{4NKjZ-n_7xAq9VP9-eIBTvh=QHU!n{shgHr zh{bAj>fjC_sCp;p-FO9Xt`ibz&5F3QHE%mReGz&k<0!7)Cj9jI?{ayM_Z3))W-$Lt zrtb%bC1w6M!alQivY{{C$kOUN%od^OKeSW+2vxBa@IcZ~#nlR|My>RsXKtc&s8-B< zmn4^`9oED$9cO_z#rBa=rIX)c4z926GDOR`uAd4-oUL_CxL0bkb_1>Kx}AX7og<1+ z>Lxz{x&aAX?!AyUjBa$lh=Q;FmCEu&^w6ucY~l8HY4za?r|a1&7<@kJ`Rk>cD(~~a#cp@e+t^z* zcVHNDemkq}W&|{(iCXa{^=d1$P1u-CqCi+)3zh&Kq7+3wM`wt9^`Lt(QZfud^+2bI z_P4Q(f72gOzsA4l4{oIx-UHiAuKC@%(LS^v$Z|GQ&8>)CTtlpUVYZLvTNmhKJWg8I zvRE1ocUhQo)rX&%jV?*OZ@$TkHp39d?Hqrj-P;{C)+b$V!4w3hExQz0jnt`o>0DI_BBy;GY`|P@gb*1!8uD#^F z;7AObMBW7$;f0MC09ccxATQthDM+Pb1=5q??zX7%{3USTwhM+Hl@G|sYuRq#@Kqf{ zU;*sK-S?9+jSq2`e)-*l{S}|esDs5O4Qz6e#fGaW*0!tS#qWZ;*1gz0Nbbo^kPTpUrC0_jTY5j_}G)^ zmtQEFQCkX{X+V#<+*qo*jbb*q6=(0`__t&Yn8i54(P0Wbiv7Kl)B#s>8WB5*Ij+S6 zDzqQ(sz1NvO>bv0nPT$|*j2R{%N2a^t654KMbwzAZF`T>JF{S6y?o0XvEbCy_nChX#kiM4GjRiWhn~OctAQ5LR$P>Y6->1 zcy{;ZA(U@_kKOr=Y~8F|iMV0;)HX58W^*|`K|{0mKru0P#hp;!4Pa%A@}k=q&jA+d zaJ*Dq^q)P#Lxy`z6wuI7L=FnYqX|>_p-3f@3f&*~{0kbcVAub|9HH2pr$eZ2K)EgV z7rc|v{D)Gaib4rOL;KjrZunyOw94OM-0=Q0`@8@81=-iA#nI4yI!X=`-}H6~9{V1) z(>gJCu&D-a(>+;2@mDC8i-&i7ury>m(DP{TxT6)}aS=$g5`U;g2s$*>-)$%3ON^7s zA7WXX&M_ENY;LZizHpaW++>m~2A?iuksdhB1k|dFutXa=oR34s@^dJ%_$qY9cxU9x z14Qn{zZe(mLP<}g4Gsu6*h6s=xXs%%=F1C?^{cxaIhP26i=&Rxemin^$1#Razr7(9 zzY3{!rnb;s)cF}?mJ!cJ1$_49I>8+>94QH!mAVaMMowfERgoIFV=gyh>4OR`>gHV- z>R9>sl!=#aVPLNNHNW$cb#F(qwbPG@-uW4`U8=GLNE&5!P?BqU_O~##Z0sJ7SS~Q7 zq?~HSdc$vm{SfM?GL7F6erpjkGkac&OqF{eR$eW}tHpM0>25}+bD^w6bq2Z}DSg+X zgmq{DBzgD#$$gG4HG6HSs~eY=Mtnbi^2&4KEcvV-R@CO&6Fdg?T(E2KN^z`wo2XkZ zXW^aUcU99LduOGp;_nL)(-)26T?;W~rmDoUUiGyZNV%r?4$Fg({GP8%Q6-qsF;t3< zG@DP==ZS9q-5m2?WzV#C$Dj;(+qz=yrd97fxyy2YPJ1cTId9HZ*#70?`9CkzAv~K? z*w(@qsbXC+m)KT5zJ*2~cR~+#Ie;{i5NTCBMG__93*enoktBHa!+id_`YPhmwAu8# zk_DA`WoqJOf3Y|>TUkD)+Z-~aDJjaX-+(2$nKmk&8f0ku#`4W61QxJBS7JiGC*je( z8M|y~m3lw!_p@C&n7;r=zUwcENtcWi619z7OX66IV*cNkB8(mia5Ajt2 zzD6ONObO%2sNv+)Go|;+?lYfk27eADQO_(1V%13Nd0}G}UUScTmR8zzm9Et(M$j;% z+?Z*%$<*fwkYL#1Yh&i!AD*|Sl8D_0HU7!`?)HW^uKo6MPuTDz=-U!!$9Kg|Wi-RO zh40KP2T48O$T%-ER+4jWy_~`ttg)vEJVpvi#NDeik`)tj`sFz@us~Vz2wl=*(!J{e ztiR_NpVg^zXwqVCO}>(Os%07V;W!l+O4 zc0Pt`ZAI#zrdH=5W)^$Cu?9s8Q#sbQDuM|M(J0oiJL~$N5+&L^u|+)|`H~tM2`tk% z{toZ+MlvVFmK=|Bb%FmMdo5yg?(L)2f}P& zaq#|Tqw#)*FTE+0bc0ZW#5F_rPi-x3F%miP)j2X)6_eQSOl$J}4brOmdB<=HXeH9c zEN~#5Ap9(|IYVXk#ewm{{^+1NBTF-r zKD^FNK0}44bKavg;$Gd%z7o*Bj~(2+$OmzDD7Rinv6D~jr>5*)f6&wAJWOM6#@z?J zIP+?5Q@n|2h7%&IWi79N%y2XUmBW-O0#A3+X90ERHxZsgtP<3>zEkh->2U)#fshH* z9InCD?$9ejDIiuZ3HwL^k@rC;)Pht7?yFCRC1LYBNX3-fK)m6GFhrh)JGcA{&Ee^YjazGdj_RCNAOJa}EfL8u(D2$5 zV5l~;ui8V9dB1e7ET*c4{&2lr_OUI%z0`37$7(q6Qw>~Jl!jp9s7YK_*}l>*53JU+ z9W<1}cIJM&+~x~kyS{FHfrE2apoo%yR!Q|p-pdPHYh6&MLF-W|LCEAci+BRn){Wr1 zfH>L1`IU`|zZ5eX>9QDO!R&eIUwXSSxw#$k{Qi2h8;O4ubPW6b6m7IHvVX!1|D$dA zmkIcnhW}3j>`tEq{(0j02qo_`a$aN`+XQhx;NWi~Vv<S z@+T&Ovhq)qn}Ns)EWY)4jH<*|i$|jdcx%m$2)BNfm$c})Gnof<#HD>}p@>&=rQRj>* z*689|Arq++oqLYv$x04U&jOUR9wA*lvPW=iBfpiPZtcr@zf7*$ez&{Bxxv&(@Gy;P zGSNx(?+)ID<9re0BXVThuUX(X0#b#muE*0-lrQJFt!;lPO~J<+$pjwA(nkqrj_vPxkqRc{kn*psy zRT#gVw;kPIv;B44#Cp?{K_JtyUvC3?>GOz0^P3yV}chlhFA(Xx$Ss4E*J- z`;sTNo{=PEO24NLTFM|iWih?YzO-wgDQn>7 zNSpYQ;T3h7>I@G2FGrzniZ|+ZzUn6Z`g-iH)$d}+xc)(%dfBY(-)AUgzH$?{lXNQp zPHcZz0yh63!G^(Q7 z6z#I>Ia@z!9GX2Y?B?>;XZr&HE7{0dFvXe2kj;R>`GlkeNT#tA+zb zdxwhib=xoR<>eM>h3*(VA1%QiB=>_e{nq+v-`T3Bx-Tq+RC`piMBc75k=J|~LU-4# z8dzhI;Yi!K5rG+6Y1m3n;LT>LGk`3j*0I-WcmL_a8NWvD+S)%QrCu}FikVp{Tpy

dyq5RXY3r{`9ru9gvbU?Euic66rJ(uq{$anI%(<-=_xkHIE?3CT-~R8v zQGWLq{Lh~C|3-WM?*%xJyFVczyN-2|x9F)|Xt-U4yKZTS#wqj!-gF0EqjrdMfBpCq zR8X0ftuplwgSy`7{vOZ*E1!V$Z|8j`6pK&E%e(C3qcV}pDjjp1iG9GX(`%NE$%N6A zdGcSD)bTEW?3s^V;OBAzKDqTNql$Y>%?a}6g&CIj$N%N?eYI=bD`1uCxXt*w^!itd zsI76wh3FlN_tj5B*Q0gApgqkD6MbJnif}3O$?>#7ls!KboiW)J_)Soo5RqBZIV~z1 zREhz$7R>4{6Yi)q#&W3Ju08dD;bPx!o@1Los{QZptbB0@s*|AR4>3*8EFlUm94mDNtZ*@#?tqS*j*!JY8&5JdItf zvCSy6S8KI557lPmc^6?;0}#(_E>KKRrK7g$PPcoQ&0qXZeD-;)d^$oX9+fi!Lky}b zSQh0{s6WaxqRKhx&gbVd997_0pfxQ%5k^f^C&--c<90@%axvO+p}Pvbnc&X)1NV}5 zJ)4p8yctPS%Ru{-nfo{LbHE;d)Nw9sQYaz^U`HePdxnB_S?>DrY2&H=}IP+ED1h;hjGU!T8$l zL96@>+7Yoh{d}SgiuDc)v{kpiM>wyBd^Z;E%#DZEkf}|II_p`ADJ`}uBZWmtEd7oC zJgnacCH3~KVKolu9A!-^+;3ExicAiyd(9y{L{PvQI2E&He>W8@nd3m8M$IzuJapkjbv1%k?9VbO>74wxLJ$@i-rm!Nv z?~{+a%MGVWW&>V3xJd?BXn^BSJ&aswve*O|IOF+Dryi^FnLe_qwqG9Fpr*KN*O-wh zexuE${GP=Y$;mblA|0d82rSA}kgu6^p}?B*SW8MwP)OsYkG_B4ujAUkO>AZRs-Vb4 zYo@}s$7&F(vFMt>y8yk{dU5fAqLf3T4a^HGOGa4vdE0v^N4FB1Kl(^H!+BP~+15k7 z4wPoYLtT$9=&qe}-e5lATCyg!a$0Ee&{kw8b-YB;!THvx*=K=B2O4f*I5{yB z16XkcR-oEI)YxQ9McC1@Y0%)Cw>(P;uz*p7{j;eTld!xd{g<$J^Rd!Vj!hec{La5V z`?+ryxK36rQ+w?K z*s>4re3V&32h=MSmnc1DP7^U#79<|XklJ<^eA9SChu1f-oiw$}4rux43=Ot|`@(}F zONOq}*$rf`$VQFty`Ikx@Ts{3Q!UjtZ7 zep*mZY5UcZGH(@%lXcpZfqA;O8L{TA?yT(~2ZgQORFD{H=rLF-Ce#ZW3~VJNj1>RG z$)ERB+aFT7soS!{t?YsC>{Kt?AMGEqaZ_55*0c=uW&7a8-x5t z%}2Kqjqg`NRqsr(k==GEQir{f))qC17)3bEAEEc{aPa=(4PM?wXn_i7w^bTrUQyZ1 zGJFzW8P<*q8LW_sVTxE&`QUy3>U_7cKZJbw_6x3LY=}lFzcw zMugM}^BpAIP&K$)GtkTpB$3HvWWGwHw@}_zly6ZtV+6cnBh}_smV-rD2S845a`z@d zo5B6&TME`m8DsLCd-4k!+}uPS^xrWX;(r<3_{|kkA6J^|u)yzs=s$YL-7HRSuMSi0 zr+pgv?9lv0Lru`_9RtC~2Q7yUSosPoEw(XDeiwrvhdGL>oy;T!e*Dd0Gj^dFab7@y$?>`v;H;i7 zxfU_ExobWr)vA^7(cyiCHgk|?*D3_%34kQOETA%kq>>ZT>1M`G*lZ-6n?h;t7kS4j zjEX%9|M~Ig_~tNh1q14i>IRRB-83&;j;x$xT@32n4r}8P$r4)hEn>GAcV`|vCokZ} z4ct*Qww=p1oa5C26E}xv&gMbBX$hz?D)IvpRC)&W3(|I5)%mjG47wM>gRwUp-WauH z+o2O^8nO=z$(+*B02vn+7X!-wLSObT`~k{mOo=i2dl) zE-9WMo)QSvtSm(i_fyJNxR=__pAIH45nfB~`zzm!S%`ks)@j-Pn^}A;9IDn} z0c-@8zHN#QTxjBw*8y3#VcQUc*-q?&W^w2SNvt79fm_;T z@m_X=Oky_ft#Uts?)uKWyoindTFer$5wFyT#dfc*KAzmAKJVXladpGq{f$PoR~C0t zjy5AtOVL!H8Jd%jL0Sa;#^PUTGyj6ke+%3M8^q@DZSqmBPdMD`aX10$HK==&5hX(1 zrV5ymngO!9`m#s!=GQlkG{8G`F*+}X-jMor{nx*%PygMljiM}Sj@S!HAwoqAL|SA$ zvQ}%v&aD`9=gV8H=Aw+8s1P$xqB$fhNkUz}R<`!VsD#(X90v@ty#_GCAOOo_wKfH) zle>8m7>LhgMjkvtB_A)+ojVT*?~3l~K?aNb5C=^=SYs~nXX`b0B}gNHEJ;hBQ&G%O zKeHwYzy6juSfoz+v}5oyWKLGr>tJaC8n)l`xII(Z<@u_CtQDIzGPr1A3?TpS=}jUa zeLTF&8+_CmpE4HCUibgrP6q^(@%G}Qu?Lo*e2ji)1gywm;~npeJn16jmg4&(d!uffN_wNS*bzX|IN`E_YRB{SzVZe5(B-%7YM zVe5Lm{TXipySxwNQEYaigdKE@n&ElMSV~i|z~PYOloV1z&YnGh`y_$d&ec_SP5yJ` zlVk<6)Zj^l5^pDGqnI2NfhnJR*kz6mbVdJ_R^yHOdB%3BMX zci<~PX+XFz!Xr%f(f1IteG*znM!}SO&3~i#u=qvS`}a4v7lq@FJ+dNwu#mH?b++Pf zepD7>QLF5FY`=J%*sHpH&lVJqKDjtY6ql2CbuBqh3%uM|0f^_M#|6<1Bt;6L8{t%h z$D0h;!B784_5*2z)gtu@Fxc#rsjbnPOa(a+q4w*=w~h8}QG-IUT}u&g6(?}`xD}TY zK_hH1o0*{e#C#=&=`+%J*g&??h2#YhOTF%2@O_Nat4y}x^W%rE-b11NuwM^PhSntO zbvf_3*OpCB`soK^5lePn-J*5x8p{v&eS=L}){>MzBw;_tsgB;LL$Ba%8&KfD=-nkp zWs%km?be~{<{7%^nn&~C2SdpaF66=pw5UiQ;=v_B%oFxseXxG)ZdS#xS6{JMqW8fQ z@33*j(j?`U}9(f^MSzrw02?r`D=J#YEnHsGLPGoat??6I3}d7MG!Yx`o9PR zxk`9n=EkgPOlM7ILL=$ZsDUi>PVNkjPsqHeejYX`4!1`RI6Kw2vp+6>W?tyM@yV>; z)WTxAI7zH5;}uLTWG4@p?nqVI#s#%*l9x?LG08 z`z$`Xe?h+~>|4bBBKsP1;sxZeQ5gxLrXpw*!}IP`nbepXr9%e(M`)&!->s6aciw_BSo=6}mP`N8?hD)UpfbQ~&&r>VkiV@Bd3A5EwlB z%c8p1#z?-TLVRTh?*XZfgjuQA4BEnFd69wFu;@0znew!0!?S<3=0L1BGZ#Gh8Q;`g zugkPV5UE=t2!WfnVgfgZc{N&AGc6IpN-JWt%YMijz@D}9-64$(@3Pr@a65ZgE#!a})D z{A2Q3$7j6iL}2UX2q*7`A)zVD;$E!j4C0FgYOj%0#3P%LhM}vmOl(bAop( zn4>7&81tK%U7GJxlZ*%=t|L8oYf|s}9fQn;#S@)LI_GqS_)QeM;+~rn^ZYgmyxxa_ zNHf+MmKd6Xw5z@S`8bfaK{R};6YJ}a$+~<~<4$sNioJv1=y9wANIHeodcIZkM-$9o zYtKa40qy9}>Bf0CrKfhOGv|%9C9up8~Vs(0Rs+!wz} zhpBv-oSB&jD@5H|d~_~e-{C}_Xy6Jghs&Fddfjeb++p)BXR^}L;v91jwP3aj=H{;> zhLmJ?e3kf>QHS(q>B0KRtV*v*FUX|B?R-ZPx#-Zt%&fR$Z7=fOwRGrhUPX!}+Bx8A zXZmB=IIyO3S5kpy9EchPIg5nWDr5`;Q=xflScp`QD;P z!=Bp8548RNT4~2V961N%gG8uJdu)!)@B2>hPx{;G!J*`7wu8mkj`#XOrY~71Qwqry zU3sIzyp;MA*(@d3cAt5V9>33}!M1mDzBKc>y*8Dx`NRH$cv_eC*m3>3Oo`h_xG50r zU`4dVNE*4{QU?`oF#jUcG?et#i=3J% zSc<-h74TU%>{d8Xg_FG_-wJ!M?15ec{ywgR*{w5xe z_^2rF@p`f^P_1RF96#NS?d@ni*gw2KkrS*xgS@=!d)Q($O>ef_7K+_*Fk>psAGU4e zMi+~uMQikV3V2A^n|bTz8J3I#Ikan}=$E)A{wQ;S&DjWu$%O7~K>{ph0{*^ zhKh-;QP_-&s!>QW)95k-V>Vo@_+<|ZqS=sFp?967F}{M=s97t_7FC!zttH1`#4!C< z=OVz;StjPa?JnS(k7(jXm1XpaZYms36mU?g>$^m4sT+A@|Ep)}3RT@izE}#;**+ci zZ8yV_O{{bU;Dae;3u0Jb(>9H;AvKWjd!>0guG6U3aP9WNfp7F{|3qDkj)~c{j1TVoGhq=+ z1G4%U@8)zG%XnnJ`jT+Ijo)`JFVaF|bxr*hZS6-Emhqnj4tFZPXRUS29#C%kdUog1 z$kcD;HEQ@%I4)#H>)$j3^NGZapo3@$YKZ{_BO- zGNXQLb5X!1Xca!>^@J{sH^6XKRjjP6^faX1c;M-2K+Ptj(P%4fefG~UYk?RhU~4IE znXqHLTtv;B;H;d^F@A1@FLOT72Uc0d^P`!ZO+9 zJuY}FW6ibsQ2X&9GBt6_y7rE@eK3^EJA5bzSxVDVgs8C-*H+>%)=#vi<0u6;T-6lUdqljJMidX$ znK16ACGXBR=h@$1gtjH=C^?L+E+O=r?-+W`N7vWR=e2^zBV{!s_+kbz2bCdPi~g&< zDKLvVI*C-{fMk#_#^oQN$yT8>wPs&j0IXnzUdvEJYFuvB$4q@ZQ`)9T@^{%2iXD9%<;%?2N~SWa z#Z+^=lqkQzsHwBmen%oK9;%afKTR6%G8g8q3o}`aE&1VcbM`mtjXP!Q9fg`5xxppQ zr5ZLxA21!^a&?o1qHwx~(-OKJ9on~=sV@0uf_zuyr{u_C_^1ukFa%0Q2PKpwCTckx zzBm!$u#U16qXS!M%1-@ZA7%L1veVUx@wx4LTk-13Suy<-9<(i>-xww1# z*U}c#_eb4S-q-Lq#NX#?Uz`cqc}v_x54#$NG04yOu$KnxT>zHW_VkQeU1>l}=MDKi z>1r^03|DVnrGoAA{1X*$ya;eRfO`~6bI(?s1DTu9Qnq8lNxqJE1pqSMm+Tw3+CAXiB4wsEf!Vsl{L>v@sYyDQLk+xM!g zBDxV4E>b3v0u8GVuYyw1Og^KrDBPmGK#JGqP_`20Q9DhSQlSi-mTKS_X3GhZ71u&9 ze8~OD;Wypl*t5>IaBNzH^CB!_OgNvw zl;C^=$j>uDb6xmJ)=lRjj!MWaA9mN#(DsLY_UH)Kbs?XhxrTace-;BIt<5}9KMBQr zbU{`_vX-?{_8$|+S`35QnRbwLxQf16!9wb}e)4<%?@fjon9;uXMd_^ar3CzWF`t-X z`k|ufl7MXI=B^da*=S+zQjtPKADPla^U_N=2)J7|ttY)WJu5MDC&zp6kGny|HBnrn zs=WQoN;zY&>NdGQwm<&)6nh{a!glHAfbr z*xG=|;@eHU5XIvmcF$Lv4JyyhGwvd`u(noRjslQ!b2siLvlWZ!4@Yyi8+asUJs<5; z$6S6mnO9+n@F31I{oq{LHJzbCksHsC*25N{GTsdSV}dyR(@3q3y%}5lve7X{wKU_| z>XwLpC*QDbo$D1-bIv=i%j(aIgO3sp-aB4K2QEKUFAur0t3Mm-&Uzg1;GQmW0RZY0 zQWCver|rx+Vi7)Hdlg?L&N*;$Uacbf++6^hrE`!FwG%)!bM5qXVrui62}vpkCfa`G zC{?u!vv6f6eHCj1#P^j)jxgv@wE(QX*jT||{<_=k9uAr(^XZ?|PQcayg#S$RLo@|Y z$GGmq@$SUk_NAgin{d!JnzswrM;suosRCuRj|sKV1fZGtsBr`JS8kSo2apte{U^+d z7yGAB>%Cnc%?0__<=lUnMhxv2PsK#R;BG)*EE*LRWmsZFPs|c^1v(6fi;G_-)R9>o zx0NV%&65l?&tUF3eG2cvYM9rzwxcxeZ69}5nei122HR@fUJX-jTDjFl9_4Kg?qg-5dh(0x5&pF(CviCmuuT-R;zB1Mh|QI;3wa(;Yh{ko1ga|mqfQ?aJQ1B!Ih z13R3{Tc%1Ya0~Xp9811Uqm)pRz-LbE;+sj;%5{YuC5ofspDX%HDslY^3EYvJtFrdO zYkKojqEa0ALi+0x_^eSZM>R{yOnGm^M_Ew$h7#W0G+_|V<5 zAJ^6baDjK0<;yAV3&Hx*1Z*}<&URXB&C}0MK%`J5=uBGIY@u5S=2B-$k^RHrH}Q!W zAjwkQ>6vC}7MJum;{qFpuYJ=dU9W7yWl-61Ub`-!W+;0j$5)2c^`=tEq=jKMm@+xq zJz6kY)XD%owy<3w+jrK)Mo7rPP%vG^ap#oyce!}z{$~#jpgteiJN7G#2~$Cv+LGQ* z>9|M}>v%yKwGS^KL+xks2DvdpCiZI%n9#2(hSDE5epWm4<;17hFcEO~2)m{C1-Ga3 z<>Sp@6I1b$xq?6$&A_AntDe2r4sBNwC?B zZUI_24NWD)4@AyAk$!@`Aa5Z49c~!^6OIz13A3L*D`xzFM2jJJhcA^ozp?O>716Y~ zdJ3KOl0B}e)}gw)fL<%0268(;sZL{4(oj|Rkvm_g`H|oeBUG8YYcw#>Ydo%+VEHkc3B$Ohp~RaO>y#a) zqPhRQH35m+c~b2kvpky%mOQT?vJdjRD$!H-0#FVgR3vI#-L_5RHQ$asR$F@QHp^I_ zg!^GYdnPTYb3F$Exh8$qZ9+bn16zaAlsOfZWX1GkZIsCpG|NZ= zr+$-BS}{@8?eOdc3KJw(2KxfajUjwF9Y3NVOVWcQ{UeelYWqJrk*Cf%zmS{1>yi`O zYDv+{Z)IaNp6?B%ojbRSB*Q52ysV5Jy4G|48ox~yFqUb8Zk3@Q>AnF%u|*MkO~1f_ zv%Z(#S?hIvx@3~sxqHNnRbB8y$+z<$arx+SC{ozXPPx?LKFPbx)qq2a@+GUf{{D>P zoS;Cw>aw!5QP{#ul_HD1rJEp{hQG=n6(?)ZS8I7zDvgsIwz-hm|9El=T$fcgn#d<* z`AGYAc&D}&n}W-gf>+8sCyL^Ba))hO`w@bcf*CWpE-c=bUp^%08aK{#U2V_p89w3> z^raK?0gkl8cYf_XIviYplPzRTqHSmMNw#HA`B37WIEjl zc`56%L_->`;y|D)%=7!kmLR=&@Xg~*V=na~BE7~vU#do$BBABwE*d^r3zQfxGnTs9 z*j%V{)ydrw1Z*7WN~@=kH|e^6vVJ)DHSUK*QYD>#W2!;vR^x34DvsR(drJ}CP3(Sh zD+!G9OJ+pdL+1uJ(ia51had!>tQpl;?cZEXoULXv`>s##F5etC=l{LicCOG6B^N+R zHmu!^{ax<3{OJOpI)d|gGpo8qn^%mNJ?94m0CNcpe?%IEw+_%{;+B(~bH8{;WzWi& zh^&ACIOkD5$?$92{uYZp6>}rh_Vzvd^aZ!*`;k0qv$$h+j;hakDz-H8US$HnSHOlm z^PTWh2h3vg$~rKZ>tCQb*ABFkc0otE8x7H`2XA?_0FIxUn%Y+))1+hY#u}oE7DD0* zNhF7zKHnBT0N?-c+v8CD-Tzbqwb;wpqRwM-;=V;-PV*v(@)w^E)N)X-设置->文件管理查看 ![](./doc/images/setting.png) - 点击**设置微信路径**按钮,选择该文件夹路径 + 点击**设置微信路径**按钮,选择该文件夹路径下的带有wxid_xxx的路径 + ![](./doc/images/path_select.png) + 5. 获取到密钥和微信路径之后点击开始解密 6. 解密后的数据库文件保存在./app/DataBase/Msg路径下 From 01a4733b55d1f37992537b712d68543592b65ff5 Mon Sep 17 00:00:00 2001 From: shuaikangzhou <863909694@qq.com> Date: Mon, 20 Nov 2023 23:08:10 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/workspace.xml | 41 +++++++++++++++---------------- app/DataBase/hard_link.py | 8 +++--- app/components/bubble_message.py | 28 +++++++++++++-------- app/data/icons/404.png | Bin 0 -> 1994 bytes app/ui_pc/Icon.py | 1 + app/ui_pc/chat/chat_info.py | 22 +++++++++++++++-- app/ui_pc/mainview.py | 1 - app/util/__init__.py | 1 + app/util/path.py | 11 +++++++++ 9 files changed, 75 insertions(+), 38 deletions(-) create mode 100644 app/data/icons/404.png create mode 100644 app/util/path.py diff --git a/.idea/workspace.xml b/.idea/workspace.xml index ab978f1..4978b11 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,16 +4,15 @@ - @@ -675,7 +673,8 @@ - diff --git a/app/DataBase/hard_link.py b/app/DataBase/hard_link.py index 448199f..34236ec 100644 --- a/app/DataBase/hard_link.py +++ b/app/DataBase/hard_link.py @@ -8,7 +8,7 @@ lock = threading.Lock() DB = None cursor = None db_path = "./app/Database/Msg/HardLinkImage.db" -root_path = '/MsgAttach/' +root_path = 'FileStorage/MsgAttach/' if os.path.exists(db_path): DB = sqlite3.connect(db_path, check_same_thread=False) # '''创建游标''' @@ -47,16 +47,16 @@ def get_md5_from_xml(content): root = ET.fromstring(content) # 提取md5的值 md5_value = root.find(".//img").get("md5") - print(md5_value) + # print(md5_value) return md5_value -def get_image(content, thumb=True): +def get_image(content, thumb=False): md5 = get_md5_from_xml(content) # md5 = 'bc37a58c32cb203ee9ac587b068e5853' result = get_image_by_md5(binascii.unhexlify(md5)) if result: - print(result) + # print(result) dir1 = result[3] dir2 = result[4] data_image = result[2] diff --git a/app/components/bubble_message.py b/app/components/bubble_message.py index 7c982c3..fc26e4b 100644 --- a/app/components/bubble_message.py +++ b/app/components/bubble_message.py @@ -8,7 +8,7 @@ from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QSizePolicy, QVBoxLayo class MessageType: Text = 1 - Image = 2 + Image = 3 class TextMessage(QLabel): @@ -104,20 +104,28 @@ class OpenImageThread(QThread): class ImageMessage(QLabel): - def __init__(self, avatar, parent=None): + def __init__(self, image, image_link='', max_width=480, max_height=720, parent=None): + """ + param:image 图像路径或者QPixmap对象 + param:image_link='' 点击图像打开的文件路径 + """ super().__init__(parent) self.image = QLabel(self) - if isinstance(avatar, str): - self.setPixmap(QPixmap(avatar)) - self.image_path = avatar - elif isinstance(avatar, QPixmap): - self.setPixmap(avatar) - self.setMaximumWidth(480) - self.setMaximumHeight(720) - self.setScaledContents(True) + + if isinstance(image, str): + self.setPixmap(QPixmap(image)) + self.image_path = image + elif isinstance(image, QPixmap): + self.setPixmap(image) + if image_link: + self.image_path = image_link + self.setMaximumWidth(max_width) + self.setMaximumHeight(max_height) + # self.setScaledContents(True) def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: # 左键按下 + print('打开图像', self.image_path) self.open_image_thread = OpenImageThread(self.image_path) self.open_image_thread.start() diff --git a/app/data/icons/404.png b/app/data/icons/404.png new file mode 100644 index 0000000000000000000000000000000000000000..d16f27210343876fa927c10082faa5a2f8e61b93 GIT binary patch literal 1994 zcmcgt`#ThP7yr%;5mHgPKP{1l31Kss$$gqgnQ=E%6g5ijWEr-RxYlksAn0NXQ-kRkyS!rcy0Dvsc(e|R) zQof6ngjjc3UFsJbNccquYfwL+x&(k^D$e!{A%?`EVX{?-``%8!{X^%VTJly+ttx^Z z?BmfAoP3w@kdd|~XMQ%5;0VvqLujkv%!7_sU)Cz0AwLi8^0YO~sohEquv@%uoOQJz zXVbLp7N7IpOI_%_#PC_`7%$-pJ21jeo99-|t*l<+MGLW0PFLR7ZB75(W{x<9GG2=< z<>TAf35V-CWfG6b{j#wTCIFSS#-B}QagQ!OXJ2oIo`eDtRrH6+{)t+m|5xGeg=do- z1mFpG91aVvvyjv+#_*EQKN2agA8>`IHs&Ub5b@KZi50#uV0X4~^w%&y-Ju^q5U0T= zzmDK<4Ys^J%`@c=_9C#&UM51Ju+pvejAN*q6gpS0}bL0 zBPnS67$M7!T>Far*42+;3W;H%A>6$nvYfWc;UH*dRwZMwc}YzrS-IOmBUMv8 z(MW?HKVklqM|Sfq@OBb+K=;Gs2eu%lh+* z0L1)T&6Ncs-n}O@4^SBH1?}Ds`Fe`!uh5}Zd*Gn93BOAMSduGI0YcGhn=e3sb!ttf z6d;Uimmxvk7=dUD%HJ6X0>s@_)*OWbVpN}w445||Dk^}gr+6z*pxIDelMb-=eAoM+ zAl8X!2M0B)znxbCy*Fy~jslm!qWGu(F$8f>CsTtFbmp;C+-GE^U z<_?5MZPNr?-cs%1OYQyKm)E9Rr%s~TDrsMTkJPIzwZPTDDCek&(Wu52Gt74HQ0|J| z&lusX@C#c?D!IJ36iqbD>hp&u4u-^)TaATvlxNRfqP0ii?()OA|*@ZmanVBw2G~yLR;Bt5Ae9XzBscOUxix7!2j8$yo>2u>TN+k%!%6Jgyg)8gh z4wXbM%$=t%Kg&s&BwsdW1uEs$_f&6rTv0Tpgwz?Ve=br&4PN~1)7baGwF<}+%VW>8 zU$|)9wD1;L;1&40s?36a>v#5Q8h(A4Hd!gM$jxtALJyu!jj;65{G?1VGCdn?=)1W; z_}JBM-HOs2i5XtjJj=J$O=rjQhQ2d8ie-e-i=gIv-bpuF9`^w2ef`ic|7hF6em$ZGWhL@8ti{{^@F5=JL}PRLVQ0 z$BS}SdQCF6fnzXWbTEAo)T^{I{xVR(ftmE6@#NdNmzkeU%CsjZquexEVdT z_dA^zPjq+R6sGOC>@`h$F`)@XP2@4E0CX0XVTLH{y6I46u7PqYQ4Td!i`oZy!W`iSt=Qfb2b$(Own9RyT^Zs~7^HiRYxg!sit4 zOJ01IL|$@CFw~0EsE+ete3`Js`Y=M74rRNKG$yvvu&LYwL9UK|vf(?sXGk{UD|-vEC;~<4L}A|R%NwY?W*lQ{|#xRl8mu(&SSHI?aPiGB(r9( zkd5)VU&)AX|2#jE+hYNOcxKA7@1bP0n+C-m!-BS)2$v`yfJ0btG#U*qZCII4-!`R{ zEJaX*tq=nUO1Q*gxjh6rlW+K~keZ!eRRslDf^+0+^*Aw65Ak;-moGZJAtNjX(d)X> zg^-p)Wf;$#KEMH#*-#HZgQD}N+>-8R9e1fF)Hi$8;Q&_ZwLlB% z-n}1;Ffn_xb#}tehYlZh?HNwmk7o{RG4H(OirAbeu02g8-)?&}(3R|xCY}Yr**V+R ITl**f4|^-6F#rGn literal 0 HcmV?d00001 diff --git a/app/ui_pc/Icon.py b/app/ui_pc/Icon.py index 88a0f0e..9a569b1 100644 --- a/app/ui_pc/Icon.py +++ b/app/ui_pc/Icon.py @@ -3,6 +3,7 @@ from PyQt5.QtGui import QIcon class Icon: Default_avatar_path = './app/data/icons/default_avatar.svg' + Default_image_path = './app/data/icons/404.png' MainWindow_Icon = QIcon('./app/data/icons/logo.svg') Default_avatar = QIcon(Default_avatar_path) Output = QIcon('./app/data/icons/output.svg') diff --git a/app/ui_pc/chat/chat_info.py b/app/ui_pc/chat/chat_info.py index 96b3d0b..d5a7958 100644 --- a/app/ui_pc/chat/chat_info.py +++ b/app/ui_pc/chat/chat_info.py @@ -1,9 +1,12 @@ +import traceback + from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout -from app.DataBase import msg +from app.DataBase import msg, hard_link from app.components.bubble_message import BubbleMessage, ChatWidget, Notice from app.person import MePC +from app.util import get_abs_path class ChatInfo(QWidget): @@ -84,7 +87,7 @@ class ChatInfo(QWidget): is_send = message[4] avatar = MePC().avatar if is_send else self.contact.avatar timestamp = message[5] - if type_ == 1 or type_ == 3: + if type_ == 1: if self.is_5_min(timestamp): time_message = Notice(self.last_str_time) self.last_str_time = str_time @@ -96,8 +99,23 @@ class ChatInfo(QWidget): is_send ) self.chat_window.add_message_item(bubble_message, 0) + elif type_ == 3: + if self.is_5_min(timestamp): + time_message = Notice(self.last_str_time) + self.last_str_time = str_time + self.chat_window.add_message_item(time_message, 0) + image_path = hard_link.get_image(content=str_content, thumb=False) + image_path = get_abs_path(image_path) + bubble_message = BubbleMessage( + image_path, + avatar, + type_, + is_send + ) + self.chat_window.add_message_item(bubble_message, 0) except: print(message) + traceback.print_exc() class ShowChatThread(QThread): diff --git a/app/ui_pc/mainview.py b/app/ui_pc/mainview.py index cf2005b..3448422 100644 --- a/app/ui_pc/mainview.py +++ b/app/ui_pc/mainview.py @@ -89,7 +89,6 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow): me.name = dic.get('name') me.mobile = dic.get('mobile') me.wx_dir = dic.get('wx_dir') - self.set_my_info(wxid) else: QMessageBox.information( diff --git a/app/util/__init__.py b/app/util/__init__.py index e69de29..ed49344 100644 --- a/app/util/__init__.py +++ b/app/util/__init__.py @@ -0,0 +1 @@ +from .path import get_abs_path diff --git a/app/util/path.py b/app/util/path.py new file mode 100644 index 0000000..ab32b28 --- /dev/null +++ b/app/util/path.py @@ -0,0 +1,11 @@ +import os + +from app.person import MePC + + +def get_abs_path(path): + return os.path.join(os.getcwd(), 'app/data/icons/404.png') + if path: + return os.path.join(MePC().wx_dir, path) + else: + return os.path.join(os.getcwd(), 'app/data/icons/404.png') From 13903a1a735a70c14db9d422b1a76b18622bb6ef Mon Sep 17 00:00:00 2001 From: DzhiWang <2429634486@qq.com> Date: Mon, 20 Nov 2023 23:51:08 +0800 Subject: [PATCH 3/3] add decoding for dat-picture --- .gitignore | 1 + app/decrypt/dat2pic.py | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 app/decrypt/dat2pic.py diff --git a/.gitignore b/.gitignore index d8cc5fe..5379ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ TEST app/data/avatar app/data/image2 app/data/emoji +app/DataBase/Msg/* *.db *.pyc *.log diff --git a/app/decrypt/dat2pic.py b/app/decrypt/dat2pic.py new file mode 100644 index 0000000..f66abd6 --- /dev/null +++ b/app/decrypt/dat2pic.py @@ -0,0 +1,91 @@ + +import os +# 图片字节头信息, +# [0][1]为jpg头信息, +# [2][3]为png头信息, +# [4][5]为gif头信息 +pic_head = [0xff, 0xd8, 0x89, 0x50, 0x47, 0x49] +# 解密码 +decode_code = 0 + + +def get_code(file_path): + """ + 自动判断文件类型,并获取dat文件解密码 + :param file_path: dat文件路径 + :return: 如果文件为jpg/png/gif格式,则返回解密码,否则返回-1 + """ + if os.path.isdir(file_path): + return -1, -1 + if file_path[-4:] != ".dat": + return -1, -1 + dat_file = open(file_path, "rb") + dat_read = dat_file.read(2) + print(dat_read) + head_index = 0 + while head_index < len(pic_head): + # 使用第一个头信息字节来计算加密码 + # 第二个字节来验证解密码是否正确 + code = dat_read[0] ^ pic_head[head_index] + idf_code = dat_read[1] ^ code + head_index = head_index + 1 + if idf_code == pic_head[head_index]: + dat_file.close() + return head_index, code + head_index = head_index + 1 + + print("not jpg, png, gif") + return -1, -1 + + +def decode_dat(file_path, out_path): + """ + 解密文件,并生成图片 + :param file_path: dat文件路径 + :return: 无 + """ + file_type, decode_code = get_code(file_path) + + if decode_code == -1: + return + if file_type == 1: + pic_name = file_path.split("\\")[-1][:-4] + ".jpg" + elif file_type == 3: + pic_name = file_path[:-4] + ".png" + elif file_type == 5: + pic_name = file_path[:-4] + ".gif" + else: + pic_name = file_path[:-4] + ".jpg" + + dat_file = open(file_path, "rb") + file_outpath = os.path.join(out_path, pic_name) + print(pic_name) + print(file_outpath) + pic_write = open(file_outpath, "wb") + for dat_data in dat_file: + for dat_byte in dat_data: + pic_data = dat_byte ^ decode_code + pic_write.write(bytes([pic_data])) + print(pic_name + "完成") + dat_file.close() + pic_write.close() + + +def find_datfile(dir_path, out_path): + """ + 获取dat文件目录下所有的文件 + :param dir_path: dat文件目录 + :return: 无 + """ + files_list = os.listdir(dir_path) + for file_name in files_list: + file_path = dir_path + "\\" + file_name + decode_dat(file_path, out_path) + + +if __name__ == "__main__": + path = r"D:\download\wechat\WeChat Files\wxid_0o18ef858vnu22\FileStorage\MsgAttach\febd8caf62dd403a7212aef63fd55910\Thumb\2023-11" + outpath = "D:\\test" + if not os.path.exists(outpath): + os.mkdir(outpath) + find_datfile(path, outpath) \ No newline at end of file