From 95acbaa7fac3dc724cd06dc8b01de18f11ac2312 Mon Sep 17 00:00:00 2001 From: nemerle Date: Fri, 29 Apr 2016 15:51:02 +0200 Subject: [PATCH] GUI work. --- CMakeLists.txt | 18 +- common/perfhlib.h | 2 + include/BinaryImage.h | 2 - include/DccFrontend.h | 1 - include/Procedure.h | 1 + include/dcc.h | 2 +- include/dcc_interface.h | 14 +- include/project.h | 69 +++- sigs/dccb3l.sig | Bin 0 -> 59168 bytes sigs/dccb3s.SIG | Bin 0 -> 59297 bytes src/Command.cpp | 18 +- src/Command.h | 4 +- src/DccFrontend.cpp | 29 +- src/backend.cpp | 2 +- src/chklib.cpp | 584 ++++++++++++++---------------- src/chklib.h | 49 +++ src/dataflow.cpp | 6 +- src/dcc.cpp | 103 +----- src/dcc_interface.cpp | 28 +- src/parser.cpp | 5 +- src/project.cpp | 22 +- src/ui/CommandQueueView.cpp | 39 ++ src/ui/CommandQueueView.h | 28 ++ src/ui/CommandQueueView.ui | 33 ++ src/ui/DccMainWindow.cpp | 115 ++++++ src/ui/DccMainWindow.h | 49 +++ src/ui/DccMainWindow.ui | 372 +++++++++++++++++++ src/ui/FunctionListDockWidget.cpp | 102 ++++++ src/ui/FunctionListDockWidget.h | 72 ++++ src/ui/FunctionListDockWidget.ui | 74 ++++ src/ui/FunctionViewWidget.cpp | 81 +++++ src/ui/FunctionViewWidget.h | 27 ++ src/ui/FunctionViewWidget.ui | 28 ++ src/ui/RenderTags.cpp | 23 ++ src/ui/RenderTags.h | 28 ++ 35 files changed, 1564 insertions(+), 466 deletions(-) create mode 100644 sigs/dccb3l.sig create mode 100644 sigs/dccb3s.SIG create mode 100644 src/chklib.h create mode 100644 src/ui/CommandQueueView.cpp create mode 100644 src/ui/CommandQueueView.h create mode 100644 src/ui/CommandQueueView.ui create mode 100644 src/ui/DccMainWindow.cpp create mode 100644 src/ui/DccMainWindow.h create mode 100644 src/ui/DccMainWindow.ui create mode 100644 src/ui/FunctionListDockWidget.cpp create mode 100644 src/ui/FunctionListDockWidget.h create mode 100644 src/ui/FunctionListDockWidget.ui create mode 100644 src/ui/FunctionViewWidget.cpp create mode 100644 src/ui/FunctionViewWidget.h create mode 100644 src/ui/FunctionViewWidget.ui create mode 100644 src/ui/RenderTags.cpp create mode 100644 src/ui/RenderTags.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 009dd8c..a137dc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,22 @@ set(dcc_LIB_SOURCES src/Loaders.cpp src/Loaders.h ) +set(dcc_UI_SOURCES + src/ui/DccMainWindow.ui + src/ui/DccMainWindow.h + src/ui/DccMainWindow.cpp + src/ui/FunctionViewWidget.ui + src/ui/FunctionViewWidget.h + src/ui/FunctionViewWidget.cpp + src/ui/FunctionListDockWidget.ui + src/ui/FunctionListDockWidget.cpp + src/ui/FunctionListDockWidget.h + src/ui/RenderTags.cpp + src/ui/RenderTags.h + src/ui/CommandQueueView.cpp + src/ui/CommandQueueView.h + src/ui/CommandQueueView.ui +) set(dcc_SOURCES src/dcc.cpp ) @@ -142,7 +158,7 @@ ADD_LIBRARY(dcc_lib STATIC ${dcc_LIB_SOURCES} ${dcc_HEADERS}) qt5_use_modules(dcc_lib Core) #cotire(dcc_lib) -ADD_EXECUTABLE(dcc_original ${dcc_SOURCES}) +ADD_EXECUTABLE(dcc_original ${dcc_SOURCES} ${dcc_UI_SOURCES}) ADD_DEPENDENCIES(dcc_original dcc_lib) TARGET_LINK_LIBRARIES(dcc_original dcc_lib dcc_hash disasm_s ${REQ_LLVM_LIBRARIES} LLVMSupport) qt5_use_modules(dcc_original Core Widgets) diff --git a/common/perfhlib.h b/common/perfhlib.h index a7221b8..81346cf 100644 --- a/common/perfhlib.h +++ b/common/perfhlib.h @@ -1,3 +1,5 @@ +#pragma once + #include /** Perfect hashing function library. Contains functions to generate perfect hashing functions */ diff --git a/include/BinaryImage.h b/include/BinaryImage.h index e713633..69083b9 100644 --- a/include/BinaryImage.h +++ b/include/BinaryImage.h @@ -14,10 +14,8 @@ struct PROG /* Loaded program image parameters */ int cProcs; /* Number of procedures so far */ int offMain; /* The offset of the main() proc */ uint16_t segMain; /* The segment of the main() proc */ - bool bSigs; /* True if signatures loaded */ int cbImage; /* Length of image in bytes */ uint8_t * Imagez; /* Allocated by loader to hold entire program image */ - int addressingMode; public: const uint8_t *image() const {return Imagez;} void displayLoadInfo(); diff --git a/include/DccFrontend.h b/include/DccFrontend.h index 5a0a9dd..150330a 100644 --- a/include/DccFrontend.h +++ b/include/DccFrontend.h @@ -9,7 +9,6 @@ class DccFrontend : public QObject public: explicit DccFrontend(QObject *parent = 0); bool FrontEnd(); /* frontend.c */ - void initializeMachineState(Project & proj); signals: diff --git a/include/Procedure.h b/include/Procedure.h index 1e4347d..57879a8 100644 --- a/include/Procedure.h +++ b/include/Procedure.h @@ -164,6 +164,7 @@ protected: } public: + int nStep; // decompilation step number for this function FunctionType * type; uint32_t procEntry; /* label number */ QString name; /* Meaningful name for this proc */ diff --git a/include/dcc.h b/include/dcc.h index 9ede23d..c5c6751 100644 --- a/include/dcc.h +++ b/include/dcc.h @@ -88,7 +88,7 @@ void interactDis(Function *, int initIC); /* disassem.c */ bool JmpInst(llIcode opcode); /* idioms.c */ queue::iterator appendQueue(queue &Q, BB *node); /* reducible.c */ -bool SetupLibCheck(void); /* chklib.c */ +bool SetupLibCheck(QString pattern_file_id); /* chklib.c */ void CleanupLibCheck(void); /* chklib.c */ bool LibCheck(Function &p); /* chklib.c */ diff --git a/include/dcc_interface.h b/include/dcc_interface.h index 12488e1..8ef88eb 100644 --- a/include/dcc_interface.h +++ b/include/dcc_interface.h @@ -5,18 +5,20 @@ #include #include -class IXmlTarget; +class IStructuredTextTarget; struct IDcc { static IDcc *get(); virtual void BaseInit()=0; virtual void Init(QObject *tgt)=0; - virtual lFunction::iterator GetFirstFuncHandle()=0; - virtual lFunction::iterator GetCurFuncHandle()=0; + virtual ilFunction GetFirstFuncHandle()=0; + virtual ilFunction GetNextFuncHandle(ilFunction iter)=0; + virtual ilFunction GetCurFuncHandle()=0; + virtual bool isValidFuncHandle(ilFunction) = 0; virtual void analysis_Once()=0; - virtual void load(QString name)=0; // load and preprocess -> find entry point - virtual void prtout_asm(IXmlTarget *,int level=0)=0; - virtual void prtout_cpp(IXmlTarget *,int level=0)=0; + virtual bool load(QString name)=0; // load and preprocess -> find entry point + virtual void prtout_asm(IStructuredTextTarget *,int level=0)=0; + virtual void prtout_cpp(IStructuredTextTarget *,int level=0)=0; virtual size_t getFuncCount()=0; virtual const lFunction &validFunctions() const =0; virtual void SetCurFunc_by_Name(QString )=0; diff --git a/include/project.h b/include/project.h index 34b7b7f..7c1c3bc 100644 --- a/include/project.h +++ b/include/project.h @@ -25,6 +25,62 @@ struct SegOffAddr { uint16_t seg; uint32_t addr; }; +enum CompilerVendor { + eUnknownVendor=0, + eBorland, + eMicrosoft, + eLogitech, +}; +enum CompilerLanguage { + eUnknownLanguage=0, + eAnsiCorCPP, + ePascal, + eModula2 +}; +enum CompilerMemoryModel { + eUnknownMemoryModel=0, + eTiny, + eSmall, + eCompact, + eMedium, + eLarge +}; +struct LoaderMetadata { + CompilerVendor compiler_vendor; + CompilerLanguage compiler_language; + CompilerMemoryModel compiler_memory_model; + int compiler_version; + QString compilerId() const { + switch(compiler_vendor) { + case eBorland: + switch(compiler_language) { + case eUnknownLanguage: + return QString("bx") + codeModelChar(); + case eAnsiCorCPP: + return QString("b%1%2").arg(compiler_version).arg(codeModelChar()); + case ePascal: + return QString("tp%1").arg(compiler_version); + } + case eMicrosoft: + assert(compiler_language==eAnsiCorCPP); + return QString("m%1%2").arg(compiler_version).arg(codeModelChar()); + case eLogitech: + assert(compiler_language==eModula2); + return QString("l%1%2").arg(compiler_version).arg(codeModelChar()); + } + return "xxx"; + } + QChar codeModelChar() const { + switch(compiler_memory_model) { + case eUnknownMemoryModel: return 'x'; + case eTiny: return 't'; + case eSmall: return 's'; + case eCompact: return 'c'; + case eMedium: return 'm'; + case eLarge: return 'l'; + } + } +}; class Project : public QObject { Q_OBJECT @@ -33,8 +89,10 @@ public: typedef FunctionListType lFunction; typedef FunctionListType::iterator ilFunction; - DosLoader * m_selected_loader; -public: +public: + DosLoader * m_selected_loader; + bool m_metadata_available=false; + LoaderMetadata m_loader_data; uint32_t SynthLab; //!< Last snthetic lab idx SYMTAB symtab; //!< Global symbol table FunctionListType pProcList; //!< List of located functions @@ -44,6 +102,7 @@ public: PROG prog; /* Loaded program image parameters */ CommandStream m_project_command_stream; bool m_error_state; + struct PatternLocator *m_pattern_locator; public: // prevent Project instance copying Project(const Project&) = delete; @@ -74,6 +133,8 @@ public: const QString & symbolName(size_t idx); const SYM & getSymByIdx(size_t idx) const; + LoaderMetadata &getLoaderMetadata() { assert(m_metadata_available); return m_loader_data; } + void setLoaderMetadata(LoaderMetadata d) { m_loader_data = d; m_metadata_available=true;} static Project * get(); PROG * binary() {return &prog;} SourceMachine *machine(); @@ -81,14 +142,16 @@ public: const FunctionListType &functions() const { return pProcList; } FunctionListType &functions() { return pProcList; } - bool addCommand(Command *cmd) { return m_project_command_stream.add(cmd); } + bool addCommand(Command *cmd) { return m_project_command_stream.add(cmd); emit commandListChanged(); } void dumpAllErrors(); void setLoader(DosLoader *ins); + void processCommands(int count=1); public slots: void onCommandStreamFinished(bool state); signals: void newFunctionCreated(Function &); void loaderSelected(); + void commandListChanged(); protected: void initialize(); void writeGlobSymTable(); diff --git a/sigs/dccb3l.sig b/sigs/dccb3l.sig new file mode 100644 index 0000000000000000000000000000000000000000..ed23b4c22e825632649c830b28dbb546aaf706b0 GIT binary patch literal 59168 zcmW(-W!P2K5}q~n(F#hZba$5^CEblQBCV8kmxMG(gNSsObT>$McX#)Fz5niWJ)H0C zy=Tq5HM?n(CLMB0w1~L$B~{BtnbXl^X`#RK0#6kh`C^%?J9LAm4k^Pd{oPYg)lihC zgrw5Sf9Gkoi|1>Ku$i{ebvqeOSQ1VazNWp}FRZW$QkWM@F7Ltzxq`*DojgH%(gY8- zPa`y$mavC1&<1!Nd1^;34v(N6e1PVLTW3?|DwS5vpi0c_>4|U5aCO<*XNx4VFj-Yi&2lBkk=RC!*M% zQL^#TFo1G$Ca>>TDOgEb=ox577$|XYH%{7qjiwE}3=QGVxE|wCl8CnPL)}hSLo{FQ z-RXz06Q6l2{p1(T%#EdWxX1P|OG?mi>MmO(tv!==npsDNck*6u;A%MHl4ax_dd4U7Cf>)n z;>nr+3x zqVSDn3rV$wJmr_%A>5R_e8d~e9!aX{wUrE@MwS{^y*L!KvEf%#!6U9|S;8&dC?oZ$ z&cQFg>v8RnrqV)wPfKtlt4Fsix=KVOX=RD1E_KkiJj-%=LvCc>=_9+y+5G{}l!%C@ zTvSGdij>Rq+901opYha_@m8-ZOFUyJ$>U|O$B;Z$I%ExF=u5p3dhuBO@084;_Wq7a zP)cuVrMVcz)p4kmY*Z<1w;H^f^Jr8#6~-Hio;cz%#Rz*Pg42a7dXn7g(@lvlDeQS%kq9zZ;?UQu!NIG2Ca#?CAqb)iY*V>XVY``P}P=b`fyqkQ#6~v7s7gLtx5Fj@Ktzj2k4b% z@#wUh>c~hNW3MQ;b<;_quK%s~@LUR0Ufku|8pW6KBQD7CytItrjQS5R@o!}x|4HYp zi2ZK;c^%Kt`uv+bw2!vQ%Y-QQQD=F&@Qr7tDN;&zaWg9J4Z~R-KzUK!d98%x4ol@X zTB*C}jaT$1G@5^rUwxpy3F*TNSzs;c85QO<+7frMn#4mz?D8K%9BxH(xRNB3Tf9+z zqPbGg_i$`pBT0D}P4!m(oQ~t#8c_SNm+sjDJIKvEhg{TA;f+<|>^#CJ^7niv)VJAM zU55K^`Z-Lq@nM%$;h%KAedEk~y>VC`Zd-}4T^s9e9^{WHa>N(BKpM)>&{9^0_Vkcq zd1p%_J7m7CllGwnZ{}rQ+RNBIe<{iBPAI~QG>-_cvI5>NY@!GLI(!n*D6}p_@+^V8 zzdXmYn5XePC7qO?WQUjJ-S{u1shTg*Q@#d$cLt>mm28-#(*=^uOX_jCLytod-AUhB za#gKFDZ>i7?D2@@DOtpK`jRWrC0#4VO{^2Ql-0adBHJ5Ir#-Y?Sb%G(7DmV}`Zq+^ zKcy5+^F;Chcddecwv_e|PwSlbdZ<9(A zp)12Y?-?FiFW!$n)DHF0FqGDd+EpL=pfHYRa&rC5xv8MvrY50q$YEoZ{RzL4gMLNR zQGd=xY551ts|h-cj?2+7n||Y~p%m}(;oMi6h9{bpmdnqc)SK#5TI3C+RG4C|Wwg8r z^TLd9ILt+yv3Z(&u>E)t!cTuqI~bCD87uM8Qe#Uayg2@ z!!$~0VjsCmh!b+Cd0O9RH}tKhw*lxj=`EWb(WRD0W^o%@XsdlMW5Rm5umQdFvUTC8 zo|{JD8gk2_P(TLr37+ARB2MctZlO`Ep{A&zM%)G`nvMk zdQeK9r58OLuM9tWMY<$2HMy?|XLJWAL`4*kEuPiyQ9bm&$KHw7_)9NOn^8-DdkWuf zWjrNc4#(+~WR~L`JUOec2zSFrQu&(C(oI?)Vxa?dl(P3@GF=6Dnz({q~CwpL*ejSADK0)I_gESr?UG>NPA z_@$qcHsP~n*Rgy(4E2LLnU2yST1oN!kX)u%w3Oc4Tpr~WLM)D9#lvH{hx%A;uZ=tw z?+y)pokio8n!``=zxsonq^tfGRi9p`&_zuC5@8zG2&*VAp6VuT6ExyPNF0V~4^oX| zkNgYt#z*unl(vW5j*rm+Z{{m>oV}n-KG+V(SuM%kHKXQ}Q5q?nrDK?j+tADYmIIn0 zjB(M%G)J;C@c`Rpu~F-jLMK`6=dBynv{G_ba(fA%%{}ZdZsB+M2TvaIQ;G0rh+(hL z50hCRjuP=lSj5}7x^>iCK#E=cstog*=+&jA31-knJ_L+c)hS|uoz(Aa8Wqyp7LODA zkkBM_^{t#)+VG#ep4Wsqx>}M;aT@6Br5i^?+zQQcLL2*FeI-KwP&txL!Oc7p6+A-c@J=2S za$AI-@ca@fqGPzD7wA`7P1(3HrrP(kD&(^Jb}Xz@OhdYYYKkw@EvbItfeUyvuH=>F zsDEP>>8aQ7>gdlE={HXqns8ltPd)9j7uUErGfU`-=kam+H=n2F)C9A+1)r0l^miB{ z|LPc=PVr3KkH%YR+NifYIrY~W=&SK8JN<(D)50cODZfQcDOcFZU-5jJXQx71TIVA| zPTeg#`De-QU-(h`7N~rsyt2t2PixsVZmv^-CEA3>TF@GLJDMGK`2^hi=Uxk+UX02A z+1{bEz7V2EEW?9CCptsfqJO;eKSZT{RsT_y^W_-^b%OJst@!*6i5Hkg}A$y><8bj^?iyDk&!f(vW40* z!j{?-U#`dOj<>Kip^N>%#cd`<2JaYU!MlSyjpu8+m1+aQ&I@ZPlTE}tdj*~`gQHtZ zJ0Cu3bg-)yT-sC1&-}g4v>Z}cBcZZ7@NPP6let`YZU3>c6h27K)0YyR$DqcKdLR4J zvPgcM`Rm>n6QHCerXMv<*c%>FaVtuveW|YueK?-owpY?$l4-2)Gw{G@DH}F<3!ZE> z^*Rj%F3lcd2a^(Vg3^S@;Ws_#!Zq|OiyX0rzr=i5B=hwY?#?iO$o+Vt3N`R^sZlUYcD?`kQzB~nltO<$unrqffM8g^kiRnmj} z5AJ0=$r1Xa9+T2y%-Q1ft8c>HdW|l*ndW$Nu%KT28O)%J9uxHq*3>FlV@#J4VJ01+ zvU10_;xuL8QhHveab}+?UA;CFX(C;%3%LY%Cf(EOyn;&pxqGxt@ zQV4(N7FFbAT2bQ#fuw<<~)g-h4E63*XTMQ?ia!Jqi{P7 zz*N0LN-2U`+pA@X{FD|1*DD)hc@%3dyTX?tja8u!;WYOR$<1Uz!p)^SIzpCEPGahHD@?mWDVxNq^K5Tu847THuEgK) zc~s^E>xQ|JLl%WWI!3lS63wc;fOb!K z4ZF;dBMx(AKIH?fip9oMxk~RtAzm*P!#|eRa?*I6U`J>in8G@%tLK29Kj9h|*mcTi zXX$lF6{^a39%+JW{ooz>SL<%A>56p%l70{tPzz`zL%j%n3?zTyYHnDH@S~0m*-*KU zXcgzwhv4(MY%2X^Q6qYU7a_WN#7|(P95FjYp(i1JC_+y|4It1L8iP#_aprK9Zqp8{ z8xjCFEVH@cgFo{=5}7*FEZHj$skSBx&t*Ox(iIv>ZtGzv zF4t%cU)AGUlif-HgEphAoSWxB;i)H`B$H&)_&|$O_;FZI)9p(+fZtuvC+JA<^ETc$ zJV2)(>tn++RL649X2(#``#2?Kp%uEyOK=+>VfB5N{i6A(Cg;+@UfT_k`AcbmPZY7j)=ri~ZF-Bp)=rmz<#zQC`0itc* zzklH3eibvQ81$ZUdR}7E5k9EBpmHn+B1pp{xJ5{7GtotA+hFdBJ6aHDf$?lEGeu_P)*s_&85#h4hDz*CTNn+emM0tNrc2Qq+hq@pNlZIq3s*HD71SO0b~R z+QdsiC3>VY^b7t)2lHFrpm+5vO)Up?h%Vs_6era2JedFA@@UOKy}Y08v`FE07|LJZ z)5=p&*}b(UB}UC>*Ts@py2x^`3SQhd9FxxBfgOSdd)zP3WuSpC{a*w7vu5-g&Gp1` zMatpIW7~8lbl#R;iw|>zU9+mPR=VJfJEWU3F%w2!sqvli2{(=<6P!>o@uQ2q;`tq!4%){}-Nmc-+uyKK?S z=&ZHTEjoHp-^B?eGgq-so>n(eVsy~OmflUDp~@W2^FURUz9oM(&c=Tgor(K z3|id)s6It05isKbk42gEnzRH0eXW~A3$Lw7pg?5J(iv0 zL$$f#HEl3zHa11jB4GLJvW&0u2&iMVQ4tfpjt-UMR)dmxPnykYx456?r)_=~IAa&I zpwiHMUxfzJ4{Dx-%i&BoK?Ny+9x!OGl#@?{hx~-6qjzrSo7S19qB^$OKz$n`TYhLk z^*FnfkvFo*F9Z!GEDK-Z@sz~hSYCWGhcCsfN+@|kE%_*`xSDOnIb5!#c`1-#LEd3Q zLTYL)RYOBt4ZR0drg57rtwz$2<8#62;eDugOD4OdeI71Ii6>ADG> z(K=F6@>vBB)XFWS#0)JgQ*9pCQT4%eIebM8Ltm~91+gyG3%TLDq_w}oeqSzELP2>1 z1*wdG4j1_`^o*A_fS1`Xp&};_>%(YmX?OHa$OLt7H_qY%OAh|g*FW26o~Y%hq*SH} zx`m%u^bl;GEDbsRZa4^yuMzddeX9jDQjtbNMck?%X%-KLu9e82Qbr)mnIyd0H_>|@ zL=m!FqEJ^T;m>j1tz;!IL3|rx<#at5xa(v27pNh!{6qDukX+FHo-9lSs>vL>Ko_|~ zpRGJ}xpZ)DO7L@8!ax9CfF@hyPymW%HQy^cp%kyP_@O`jgx_2m>^&Zhf%=h9HtA6C z>e%4}J-{=`4&`yB4s?=1)ErzlDU|dva!p2rMbsOUa177&sJf2NfX$8d@3b6dX;uC! z?2w13ffjz8Z_y%h8jB~MA~cc4e$f_aGt9ZD5xFc9y4oUoFSEG?Me^5jm=5tcXv4rT z;I0L^rzFw4d<4AjmehiNF-k4mwM+7ypO9+S-?ssaZ>O)oz<0?ADZ@>zGO%Vl)XsiM z&GRjXRfjs%%3g3v@W_|eE^I-y-3*DqgwxY!r4U6Lc@=QK5n2ZvdKmELVJgqR@+Bb6 z#5Nh9yC8L}yzT?%Ij!A7Bxw!|5;ZLM`FtAu>6E+#E-obmWZpPLgfwzj`%4qA0$rpy zIL=AUVPy9)Ws2JeS!-Ft7f@{z>I8a*yM-#?*Wsd17pR%?9S7P4MPs!;0fy@WrM#NI zv#QW2@K>O2y!AO@kG8QGbQ1TXoK*vBjtKwKW^HT7(Vg2{1T_AF;U-k@xnQ~Fy*YRD z@<4T;=!30rwY<{Uw$N@Khlv%H+RDF_*q`xGOs@hsr^j?DH__Bkgd0DqnW9Tju z!EwH&#B5Xpd}R}q^lkntZ<8g!EveC2Qt(kAnwh{|6~H)edSm|Hn-RX%ZgW1Mg~;gK z1#LC0)t*qIs)cj%wNEp(%&3sXzz4hFjP$X=@Fr42EBF&0*f{jv4gQo5OB98UaP?5R`&N-ZpHI>LDlpDi!eWD$>us zoVxqBmdg_AWrOde@wk``qk+)1vSX%Zm1nky-f0(}6?$r1>Far+Vvmqpe!-q=OT8&u zpgH~kMJJMNknO<3aj7>I04GTiYU+iMo9cwTU<|AIOxOxVX&*GiY4n>WmBO?h>h1?T zlb&7{U9u-TMOqe9rRhc(gz0gpK(lAY9HEo&xXkXGUEdp-*s}-Q+yiEVm zcr=xZhIM?;e)D@g5MEf{u;1eIL3!@feLMU(i1NmZOH>yn+nn+vqIm zt(rar-dwE(tO#_JS>Wm;pl-bax*97jEh@hMtTCoiJ|O0u7TxP>d1%Xd=`|+?S8gr9 zML^#TfDFe_q=?A2-7?V>=&`Hd4Jll#N0^*--L$edv;4Z(%K9TI$^9g@ywwD<8af7( z=$tUtzX~7u47`P=@-vY9hOkgCa2r#K&hK=COxHo9bNqPLlI3dWj0UgibNm@&LJ(qT*q( zU9jImQ@a2jyazgCB1#Kvmlh~LI>ooH++BY1ShB~@+280b>s4*0<%dETRf{{6E@b8=4$!0L?>f97hKe9DLe@n)lFny9@y6n@|T$bM>6R{n*y^EsX z*(G{Je^XR#Ld$i#Rz~XLo+hLv;i_k_1oR3XOaeXZqpcNieU)(Bl4w!Q8E%BHIGuc} zwcvKm^hLlR+boY|01}Es{k*Lfgy!9jw#xzg#AB=nU-ps6GaTjTP&TSdYxx~WDKGlX zVOs~S)#WU-3-Rt{!rM84N5iR7TAHl9;(0;f-{h>Ji z4OBSYYljEk+5gb!G6nwmbvn(7tgEllJecv{aviD;t#vRIq$&8j9pJ81;y2JZ`#=LJ zMft*5o-dI@dnlPL!M@r9brl4QcrPVvH2o}vI9?M1M;!>hE3YvYvzIHwmt22~<#o6~;VHFUR3=2Tq6U zX`urpD;P>COXeGW8Z@M<;DfR3AiBzqP?qPwkEufsdAL0_x2NGv_#Pc&mN68uzV^&E z%V}x`SLrtz;KTGTCfv`y7RuXCQpM}RLAqjd>?l1L(3z{G9_(uIJ9Mp;67^~!1 z=*lCt7kYF}`UQ+=8D=bN39aWH_)_?tuIs~)Ob6lI^p-@D1fTsD(<&AC#8jQB+ij|L z&~8#W6bO0z9hCSF(5}X6WnkK~p&DgJT`U4Kp9lvsUbrvY71|`d*Qv4`-*m#$L5I)m z_obQi*Sri}L6*@~i;5FdDb(f(yi?cHa5}HAXcOnN)u^Ay@XD*p9owt7rK-lJ71V@V zJNN)5;(eQ|jeR>;g%8@!G9%Hj(7VBzerHgiWFSUuR1uJ8Y(|kT%j=PC~)ngXF?Lau>X&7j>m>;9Gh9 zH?XSNp`OO_Lco7NX*L=cFS#p2*3MoNd9fQdA{_MGRt&m$M%`um^cy(A-H}&%tG{RhUL{L8O<3UfWMY_Y%Vi$a z_NBZRx=kzoNVENnCzUbKd`I#d$sbS=wbk@jz^K`Z)p?LHpzu+5Q@pSMZsoVUM>B@`(pC>)0?dTd+Y^5)2hd7+&1?O`DIuyA zz5oMBqut=TlmwbRMr&w3rsOgm7_!nx9|e`J2bkb`y36UnhM)6EpY7l1zn0ZUhyJ3N zx$rsB`9|x%;gtOa#lEf8)(tep>(Dm-hbbZ}SkES|;(NhAzw@frOH)BH-pdKV^`gTq zDXxd%L{`$Kb`V~8N$U}&VoLXg4tX9KqBEfvk`#@p9hb8Q@ZTG14oOMF;M^sJvzk?B zP*ZTcV?F_MZZVJNL*NwWaefS3{)WAhoRrLpOG(Y zFuek&ppz|@Vq6PeReEX&v_6!^>DN+Qt|D`i3`l-H|0KPEv@&uZcu1%DYiL`;T_eU) z^-u^`JkM%r1}~2^Qyt!B34!LfU^e}!QSFRwgFCky%(a}fmpalB>T!43 zk7`Sa+1_6NfqS`5Z+ks@srkY;NCHgaMK((sNJ0GKK{8&Kh0=PEZrWJ>lW!owxr0Vf z4Xv)nt&F+fMfTzw$*M{Iul#fjX~Ojo3sb!uJkn1#ojTZ6u%Zd!f=%PCTs5?i0Z0R_ z@&|e@l(U5Vb(jLS{U;D_E>y=_u)sQ85vOGlGH)q?8=^@m`HZtQjJAdN)F;e_J1_&h z=B*6lfnJ86`ONUQJOq2b&fnRWVZEHfcYSXe^mmDdWZzDj9Y$m3mxr4=jnl!c`^lGT zmQdIe+b>Y!mr2d=7bmAYp*L4RezP>vDf#UsCE&X7HdgakV5g<_FAd@pa88SRRyq=f z0@XCOX!bTFM6RtRa+g;^8R#wRPD-izO~du=<{j7BGQ0o+@j=2Tt(v4 zsFO(Hc(_Ols8Hx@Z)mK&lNaG{{SC@mRPSPO(KEW}d3frns0wC%V(`PB@Z@@iRB$=- z|K}ib1w9TWH0E70vf&`6wSBeRhXszFEj6VEBF zOrjl{-aE^_kXK9VIWG}vX=FJ3*T78X+ec}R|MuScaawN42g5sm>jmICwa~BOpyv(S zffLtIKl@&q>j!RT{oty!r6e}dqVN{Z3hxEKL@Ub;q=-`5XH;`y%O2u^jg*%DevZE2 z-C(H);ZUL{`a3zqH#84-McSsJM~#@JSD@g|^*K;klXI=m+iJlBuI;(d6LwfCo5d$E zDW)U4(-KpqCz3sT6xjl#&B{}D8LGbmyGH0I$<4LxGqPuuWI8wSQcyi|f+tP}Vk(R2 zmK^M&EozzlAv}wuycXJ&fdlq|%X!$g>t1@wBjk~^5a^UuXNv^hv~>}WP|FJKz^53;Tn(kNIENY!Ze=1)o^w?E4&gYmvuag1^n8o zHqEbbN@y=*kf;3>ZtzO*)-1X_^pknClk)Kw_=X>K2h^igNF*K##gLO(t&hT}uti%S zH8aEhfj4oB`tnNPuaMnvBoQsWin&ShFok7(Umg95_XY&VOry*cIP0+uG z*nF7^99iB8IB)~>si*o14tS(cKdgh>*$`T5K6rOuW8!}6H+Vi=o}xHg1^GAh*Gu3l z1@H?7%TfB^<&lGZ1Pw19cSE+XKL2e|X$H9ZAWkKvb-T2M%C?kZQflOxTXKQ$3>b48 zw8e%}9%|NulpdPnr^u|VW4c@cNf>>)dJ$2V|RHhX%cuZxDvNUmguW_yEr zK)cKWWZDx-cU7yQ7p;nn_4L#sJkV7Z3;((+=c2E5fLuf7Dn%HMZq|Vw%W@#K!qy## zMUcRa#20K&SfSCRc=!o9tUvq~-27x-Ih^I1{xl2?#dQ>y=Q+GdR{3Q)#XrgwV1~gu zmovyR%^e!KzTYfJhBKV@5ZV;RHv&>b0vqx3Hvl1!8;+@Qw%A|!<8 zeMA#LJJ|;n;-IYv5k3$}l8f|#OY0`5%o-<;8G z>d+Z3`PaGv`fBSCK?@}g@M9;Sx34@o_{k@lrsczh@EEl{!oOtXaOtQ-w^i~VK8%e- z_!(UQ^`e!Q@Wb>SxA#vt<9Q`BrKjJqzmPHfz(x3kwna}Vf(%?IzvyR>1eu96aWpbwn#**IFA~7)1`+-N+qaXZ&RDj;O z(?57+$^!4Pzn_%F(0vL)=N)ZtYzWWM9>^G7#^wZYstkt*^;GTxv7UwkxtVH0^QsTW z>?fLU$wF;A!qj1>LRCo9{A>%Zw!s8hac73I^Sy`t@a6wv=S8aO+e5&WR1)N zqbmP@=do!xf&|Gh+5`^rONb$g?#oGAp@t8^D-HCd$Yvwaqv>MqGu1Z-SC^H`Z2GCr0ck_7%FJ+!l;#+ArF5J zGmud`Km+Na$CORT59SC(b&1VKHJ$Y{G#&cnRo#pUQOeVxFGfX9>K=MgUSP}brBIDNgLUUkuUP|^m#vyYG$^fP^feAgXXq#7~J%X2n3z;8T) zQo+;SFG=lB`JU>6efFa05*K{75T4dY{=kz^wITcvI(b8AjTOUDZR0tS(fSJS zQalGP|CUe-)3YnCMI<4x*c1PmmirPbgWN!{y3$7mX-V%!vBO;5t4Xzuo{-s4ljmAC z&TD_tF?y(pv?6*@d?5OwaANXUX8604wJ&hUQ%tHm_8GPQ1F-o?jiVjm=DgFM);nZC zhwg3d;Vs1RS3vbQbe)X!FQkF*4{cEE_oWDRphq6X?@~qN((B@3Djh-veYmah{2>{|;gos1IPoT6O;Hz#Ck3$8_u2N9(Q{#-@ zr~JS;*O1J(N8kLXMRIB>!CjELEP%w{JDl%Nk~h4DBa)nZ`*G-B6X`k4qsd^I7cqme zWkmJ-pgaz*p!!ELB{tjTD}S zTe=I$$pY{l{_6<1KIz4oX^s4>V}Y69;kQ=^%aLtp1HWcBw49B=BQL>wdRaH529H@) zT@F^7Cfq}Ub1w4xe}(TjmPCZd6dUS6Ip4^)p&R!?8fCkGghG=GCuIY)izmSTQQ);+ z!4AxIs8`#=BzXfpq7bhOOTv2Jr!~B<_6}D=D}S%gp^2pm_29+y3#)ZC5?u?CcOT`W z`4D|A|D|hkgj2pNG{Qd78eR(Tw_3O>`=~l9VjL35+2BX^MsH3KvO;x;2#0AKb=TR} zp7LWSBp=_Czia@%m&g$#sT(?527m1_;9Ga{v``!p&<3!AV?K-;0~bH0e~|>s#IN8- zUWMWnDIzvJwGlSWCWV9Wx3|zv9th6ZIb_jxKnw7p;M$#n%WxjL#YC>drDcb-#{NK8 zoenR0nC?SXr3N_iTsV!be21UpyPO=JU{1arW?Bbp>wnm<^ovEJ%fM_^b(wGAyl@(7 z`M(m+exUliz;@bZu(O7!-CTAU4DZKK44&?OR7^>W2RA*HWC$6h3O(`3&_$mxX&jFW zobiaS(krP>sj+9c73`v8Xso3Fz(4v0o<#vzFVwir2qPaX}9_8suiX~^r1 zgF2Cvn#*8hp}NauzZnuDg&rlM0Ome&lHjL3NiEACP#DKvpa{wCnsxSVt0q(r2L<+_{QjkF@X>P)GM74_G)H2@{|fT;t`K`(>fR zJ?9vq4K&h8=wY+$JjIlv*bnGo?WnL#LUyb$CFP==U!Eh$xsGzt6>bkF<{{S)zrc?v zW4&ECg=W+*y&HEzdN!wQwp`it3K}bNuP!-ggdm$ilXk4@Lhe1vzrwNu_D;g zCUErrp?`P`FSC<=v{OL%QK_mO1d^-(mJW^K6&0g|ItwYk8+uruN-C}o|6{0()_uru zw+9xBBANJSj%ss4Axo`?X)iXHGQd%9DM@|5x1v7sfc~-hV9O)0X}F3$Vs`8cqx^eX zZV#YF?WRk%7%m_|MI>lsz2MHQMUs9aww3xo#m)*{IkR5`kL->r8wdFw1=+#A_7^t%PU%I;Mnd*D>8cbtYNpV=x%w9r#i zg2$ynKgEWSSH!;7Fe(TiVmlO~Y4GNwA#-2Q{L=N zMOx-UNP`qof^eDIg_c~CVj^)HOMcL7K)zAKY5e{8Au?Bhx_w6sC$y#hK{2rzam5Ha z$vLEA4trZ~!o#4)#Nl#WSD;5h4T|G~w0O8FPr$jK_$Ql#Jnnma=Bu?BZ=up*61s~; ze2+b+PP|g$f-n7!Igy5+0nyd4=s+3;BlZ;QY(Do#)1G9vp_{(|Em(KEyb@mhYRk2$^E^f!Dhe886K&$xg0Wj}s% zD{Bme|13QLGcQjC?R!kr98gKB;rXtjW?-e$k-_MK9LFN2p}CO+EzbMl zO}79pibRuT8Te=D7p@LTdU+N>*gNLpm4WB{Z84i~xquoI&B!2 z@R{4b#B*9siLCAQvd`BTay4w1pXmqJ%ZFo_eh4XP6+ezc$j7*|ldI=gv zVeBUlmhv(qT>2th=U^*(lCGLACEhUlM;Xg*xR#wZlVg|R+Npe&J_63I^ z;d%t9a1rK10MhVlG^Y}E} zJ4jpp2??6w$p73$t}GKT^qAoS-G*A}^wdgVa;37kP>Nb$g8zz%UsGOTl1=qpej1x& z&*Uil`A=4jZ|fOb8qUENh(}N0`&2@*x22L#!cIh0Z;s8Iwor)*VSA^6_kjL)Ed=1v zHAp2S)0oIg`~f6Yo~KGq@WkBOA1t!~FOg)H%>Sfx9O1b*Beosi={2aM98nMc@eKXN zqlY>A0nX-^TH1D43(WXqk_Q`@g46ywOr=ujEAeP?SZl9w=0-wEo^Sb)d8&>7{wMVD zebBgL>M8K$2L5O0D3|%2B=Fr(GT!lC|5*pib^3~v@f~SzvqNNhEqQQaPM{M%(7TwQ zjjdH!07bcL=)t=~Gb-ujaE}|{j7oUvX}l6V*tSqSI-&DS^EObM9C{~3#Igi_$4-Yw zKD(2|@&RTa40e@ayE3wQLt3b#X_LJPW6@Ek0R3GKA9M`#?<7178Hr&wGE75m>IIvhFyo+R2Q!+7^!cm6V%0L{H3Snw^q{5LmAv>L&GQ;6 zSY^6QOMqOLdLMMmZ@i2Ccc*JhEAai-GCI5h&%KI#XCCa_7vZb?%7>xqKESD226V91 zo?{cs*K2f;r6q zKbV6IO0rf?F1ZR^bCHf9rIMEC{?{=$gZII=)d|UPZ%_IVq-W;=<7CGy znFJlG1`^l@pbn-8KXX7*n+6vkI+e!6>I^0JD4n!bU>WDV zA9RSPoP_4^4XF}paAi^+qMMNg-wP}km&+q(F;|QE5pdRbQq{V`ZGQ=W{;ZcoPk3zG zs5Rc;@W@6Womjd$bZ%ZKZfKKu!6ve#c=vmiACpCD;7A~e+0VJ=UUZPZS?d3@aCO*UGV z$xBU*Et-R(*oP@d6Y%znXS9K0Kzq#{YJ@6Mga3xBS00Y>T{;IPaJUvm1|=4qv+-U> z`s#aRivIDVz|L3TymjJ3*wiR3N5VhhtQ-%2py!;S-@yeM=^*3>)=PI(=m>nOFg8^C zgyK-UR|6{;-f{C2G}a6n3+mcq>x+NW4to#(CD6Nsn|>FLd1U$p{Cu+~q%_#iIfYHd z(eSr3`b{vd_uM?RMTak|dj$R`ZvihFEZOCNcZ1HhQd?t>VWJkLGG0HNgwy>P*|CFq z*p?G^0s}J4GR+D|bBk=9kQz*e&7=r8jvFNrFVv*y0ad^-BXA#z(mpE`IwFtw8Tc-} zHL%1!6b=rDXPz8>PI7o=_px<208?=UQf@o2#Z*$C$al1e4SR!Ad=QkaR*vMT_k@1Y zAq;`qJ{o6uG#Ar@z(+SZo*hGGV6yc=f^I7If^Yjw#k)_ysy0*i|8kdpoPL4>IvdQQ z4fZgip;~Wg2iwAtXg>AfYPt)V+nA`uuR~3!(}Qe_9K^QFI4CfI7VstLvXM0he}zo- zJ*|k{lSMYqeiTJT?9v~pB+Zc&G7ZnO32n1&VJq~~ztN@NfUPytEF4$YqqC>7ZNLZE z(gBZ7!*#*uK1f~ugeUckS7R$}9aq65n8X!5u5Y&4{vF;`(Fi{HDop`(ay8G@DmnzJ zS1Zj7jGBrgNou4edhr0TpiE&JcIv7aP@P575dLk?u;FT?|~M3_-*V+ z<;Pi0OSk4U%98~O98NJ?Br8eoDn(S66|d0;z4;WVJmb$Fh*2Npr-l@4e|}}T}mSl zjK7C>Bjm8(frtlNML9&>kt_Py3h-gOj*R@5Y}UyC+eVnF_ozB@uE?FDBi;8Od=pa7 z6Oi9s?h*Q#f5Q8&e$t6h&s%E=i{%f{7vjlBIA5bV=_LwU}~%foc-g`CWD&5yma?p{Q<^L*rvGtnQIE{*IPY?zIbozURxVM`@j z*xB<1N4SbQ2JYV23csMpVH-L#Z3hmI- zkq@^M;Zi8;A`(#wiN~qF&u2<|@X+4>aTe+5J6(`EXpZmO;^nXl_ODFwKO{9+^ipv6 z4)O|n*MC_I`{4jZ ziI{3zfr`J9-9T+;pzUOqpfHrh(_Z*DqpwC^EJQHe#z`NT_dfG$Gvl-C#XqZUx0E82E1quu#4w++c8hUUb^a%8Zo%61PuCV_9X4@k!LhQ7HB?m}+< zhpJIqByBq*fqoZntY`)o{I#aTM$QqqT+Q*8F$*uLAdjI#=$FZ%BEA;vKJ&j`&KA&A8u3_g z)Y3@Rt_ba9G40X^5{>gn8Cmb|e1k zY6*MN@GDnvS*Zv={|{8t6)8kVc?Qn|qDU-z{5&V{3!WcOWjj)CzlDTI3E-VI=x@jw z>lVE0rK#qkQS^gtlQ&SgTH0a8TL!5T+`j)jiFkMuUK1pL>&Z20;N__-9D|PN&v!I= zmRa@I>H>+Y(InVYC|RcjfSeXX7_d1OIv*n#l_P z3wz~`ghMO5>8iaYfxh+5^2j$pgwy%ka0lC2AE7F1*p7{oXY?Hs!2QEUBwFTT>h=y; zg9wZNSKXI@M^&BupL=KKCQM>LL{!uO3Q<8(iAoihL0}>ZAqJQb(TrIpVI(ABmIPV_ zY_ui0fLq&7L0fmN))wt()w%=>;8I!^wSdK{xS&C$Iwgpa`TyScoO@>`3;Ol>zUO=X zSIOMvci*$0^KR$8`mZ4)_s1Tvmrk5&xX=ji_+lgz-Dq8P>|7 zQ94bW>--e+=3GaqcnfmuGcN4-%UBJq`mx%*&Lf>K=mFRl9)`a2PsWMb+0Ij(ari}k zY+M4*nzx;sVM82kG@;LDIp2hA>u_vwJ>t4TJH;66_ylwR*ZMD^OR2(208OLg4R{zI zz`pk!$3W*W$9Jw@BVOSw?RrE$oQ*waW8i7^CwLVU7zOaGcex&LeC-&n7wX?&x9BmN z$k=22&G}1cC8ofpHXiol4D^G|u5ntmvBTL9UVRsV_gal;X3L?kztItOj&}@mbilqI zN8jD2p8>7K2apnfgkHY`{-q}w3-mi3Iga}=!loIEph=o7eu|wdr@0nj=jJS9DR`w$ ztXF41lYETP&ovo)o_-B~iz~!Y@Wi>&6*5lI4#Ez8x9cEuapN$vk2C7TGFZ-aSTw)H z+}f;P3*U?)*o9*z|U%@QR28j{6SmlbYtAqyWY@OI5V;T zd?V)aV~twuHvcJjyW^p^a%Y_6_?1?qd9oB%(VE%4wy*V!mW0P_dh z9SEC}7ruv|LRPy3Qg77xZ}^;^tGzF_z?xR*dKO+ie&aMqNga;2;hi=Zc8!O%+h7%X z3jT@}#&ysaJp=yxC;C0EE#hL>-0#%#9IIV7!2)`Y^LhAX-G-L9MJ#h{byYeSfeXJ4 z+PHnt$J`IUCO3M=(N1_Tx;kLT7-W11|NlQ3BOs-1f!|@NYdj>?cl0js4M#(>q3Mt7 z?>b*HE`pz(Db9np`OB`K>(6UvXd|^e*F;wiyoCE{UPu539kX4g>qX2>ZQ?ocCTeOV zcG+*mew1Ct8u%0&@LGMuD1x1=0v_*YfrFkdKGH@BA3Qt9iVo;kC`#XF;z9U8wmB-m zTmI7+25VLTTDXghvG}hM-T>>dcKzPijkR!}xZUwraVu=E@4J2muZn)?v2PiNjK#*2 zV!CUkJ|A8a!Z_KOk2rJ=%?+6%%W;?Sg7as1=U)9$<9f$YMwj-H7BhbDaKmfwIIP}I zaf0J(=O*!M#53oFklnL^Q5RTCG0KaY#$R z`nv@C*jkKP*jL}ragSJUkmUIhY>>CR=0f*@dQ{i>`ac1+WU; z1Fz60j8|YW`cVHs{1qODEk>C>)^!oMwJ){bYJYIvBW_2;oE0J)e4ZoYGiRgmpvwtQ zyH?{^__{3Brt4v}!4t;Uh&lHU?P2gqMS57f&Ujy|#V)#P>;bsg^%z#~T386%A&b1E zJ*rK_T034`VJtI#4%zoz*Bh`-Z-!1{ud~R|9QQ+Wf1&FUZIfdjb|{|jOt^NrX1QL{ zsvsrDjI*Ha`P_BC0Ul7h+4x#4M$Kl5)3j~c1<)Mb=#0VY{W0XxEO_{4!P4~y{c6`_ z;?>~UT4-G08YVV7r#TNm`*fW4A^fTCfu-<4u)3P@JYsH*gQR)8b{FG5F<#@JR ztONf)MgPt?TEA4g8almoN_6k}bmJJO+D!pLY1Qvy5EF9`vwJ zjOXz7bMQgC$aN1Qc%2UY&@}jPBgzsqvrp)Vl&jqjUBf>So%nq1W{g)4EQpJs0e&01 zwwhfRJ0EwBg9p(d?Jd{i*h_ww>#tgc@tW8QPl?soJ9-;DGCy~&*1v!c+~0Lq#&$## z{F@6tzIrb9z|=xhc)FtqS|aRTb=|M$AzyD{=BP&Ww_6;KySC|NuqlPbSopjA0`)i$ zde}430lT);kgU%gx3n*EXy;6J3SEhmm@-<5}li*!fnt ze&hPkvBCAN;nkjp#_AV{c#z}zk*fgK^A}y4pb2IIPA ze+8Yv7uZp7i{aCbHmdb9Cs`}c&|Y$#Vf<1=V67^~+O-09(>t}R;Z@^?KiKn*$KVNk zp?(%-$CLFho%b3a=$Gj?Iz8eOM}Oli!w2ioU&Q^!GuVCFYHY>M;4`q_EX(nNbC3u) z)@lDGGMq>1x59F~1w7JIA_p-l?t$I?4fKe+p~?FMUOQ(%s`ya8^4Ui~UD+#AU|pl*xBOSY7sNWY_nN;a#ZCZ@!oT}1AUFZ*^f!FsPfKZ{ z^-z4q_fN5}|0hna1q^MQ1@=*UpLoc+|D(>o_>W}Z>G@samv#li&5~? z5@IYr{|of$Y039`u?}||fTMRB88MRIdNPHsfb?!11NkZS^fi2D-H*W|l8&`$cj?Yd zSVM#u2ODKRp0(lMFSKK^myw>yV>F+CfE176R3A|H-&dH~uxD;g!OO?@;tmu+>KLkx z)~4d^Yr5es=jRXM?n&Ay>RFBY{kMAeIliV^&4o90-Bj4SYZC+c>#e#O%v4Ha)s^YZ@-S zC%*+G<*+2}4M}JMe$L1JkNDYhdI-2Vnl)FPjw@MrFW~w4xI+IEm-a$OQj>p8#wCC{ zsC{W;kKnUg6c7>G#7ppePdy&R#c;e^3ErIUUyCtu37^r`1jOpWzHj|sbkXhDjO#so z?@0VTMtscYG`Cv)-h5rTNpJ(bhb{a=RFGEjTaL0##`Qj5?=V`laL=hr` z62dzED8A!29@BrJ@7G{mhwS085i9v@DEzvM_=R`*dKJ#c!7e3_I{aMCG^mFk5VL{# zP(5?;eYzOsSgsAD6Fy#s$NVF}{Zib8zppV>0lx+T4?{Zo;a>){Tk_bZejkR;KpvNa z?$`MDC%m~4yRp9Sc%1W5VZ*xc9;No0edk*Jk17|`KI_!E{TL@-@x4*7z|!5JJQu(^ zj^h}d2esFGyffCmGly}66|n0I@gSf1@mE)K%D1>nW0`8;B7W~Y*p(~zJ!@v7r_J<7 z9V77W{dO7;VBe253J*dLEJ1KD~fw&4nM4KtzUj@%IizrABYVF7H!S zZqMO&&gH+SDZ~J68P49(W&o3%{R-97;f$p}5iM??3-KyHufZKT`w7IOrQ-zr3uA}# zvykWLn&xaFR`B2DbZQ@Mx*K=j=Ia|oyY>#E$k`8}ANho@-qe=hriLA-g~*>~Bd8zV ztx6>jA13)Gwe~YM?mGKSh&uQy+^DY42WpBwMqQQTib`~*s^gEujf&!6+_@A_9)*mQ zOyj3oyEsZ+57en8hT9L%Rc~H^U)kR;!n478m5q=iuLJKNz%E{Syo@WKjd&e#VeAY0 z#G0vyF>L|64RLw+r-88>b?lqlp+pL>IdvQI*rK<@NjvPZ?DDQ z3-rIjuJJ4Qi|{RRz4{f1Pkb3}WxzKIvkO0b9R9|cx}2lX%b^jG@6g{uKK&h{8HeG= zHUd|NwA;i*Ozk^utbV-0zK18T;qFkzpADS{pWx3|uw}}lT)S4SAt$Ljr#hP8-+7R) zG;OLjNPkK_K`nD#$jf26)!rTA3{WP{`D4svd3upPL|g@AC%%D$ydh$td}EJF|M$3l zT4dqsbL~mQ!of^{J5MQ;fG$nqlYgu1XF^nHKf%l_#43&Y@UQ7@;D2l*c8e_BlOtrE z_65#~bIQc8LrfP>;5Q17Fkz>0EIclSsL^I1r%gEfR(#6x$jKPdyR_$U2YeSag?{KF zg*e_$2fhe6AFqwYh_H@P`pt@N1J>;>+|E#sV$io^Cc)Ex6)A{(lS9~DGIB0f`AqJVekNpXJ zyabI)F>HN*K>R{^=UnwG5Q|P8^|*6|I=5ts9iX!s_rJpLH9T{_ug#6jY%Z?|i$k*; z9WTY>E$7AKQ)iibO|OyFc7xuz{hBuv5h2QJYwIh^?aQfiGG@CpG4fENbGxJ6e6{&g z_xQgw4K~XofrY_{=vrySX;e9-2n=78}3bXHbaQJ}>1semRa|;@&nPAO( zb)VU23|`?qROI8go7??0fr!L0j2mmO&JaeFmYUu}=0}yaf$~rw6jG%kYVBo4UUQB9 zx0DVdD$zmai4JR2$wZxFKio7#pfj}ix>^bkx!WR@^-&aZk#M}x`j3(Q1vfn!J@%tk zFP9?I^tnxMW@RK)TM?Rv%II(-VWNh+rq6f)v4lpr-#V$s^HVqFOMSD;%IkNqerq7r-ZIZ-DYR!Ay8@&hP zaWu5)bGCR7q4p%TP@p_wQ%f{vCK`=IqdU=XHe|god(-e(A>iw6SY2Qt6}DULTbB|R3Is5?s2rfS z(rd(~bgy1&1r&iN2nmawzG*o1;ALm#Et{_8Eo;*AmN7LN517Fewb&>HD{$s5I|eW< zuZ3YKsT?+(XsgdS^m;4@xfe7^v}B+Y*wlCwGAwo+Av}~WL#P-QtXgFbML-8m?IJlg zY+fMHpqk67fo{J{En3IJ5CuJKz1bkno38|E%K6f4%rO^dBNt4qz9CSj5=Gux3*Bq9 z{cH7Tzcu>)wa%!%M#0c4tCFzX*diRuS0;ENT^8+OS7bi(QVV z+1zu7@-VbuvK8vWfXP-+D~KAdG-PH@nKpUm^jV^4!pzG6Yu=mfE4dFXh6$;~d!WU) z|2|o7C~0G$G9pGSXzbQV)+Hy3{>HF?m}TAX_RFxQP!OGRL9GZ!kZ5kq)1!K;q74GW zZ^VMyT5%1g6~JPXf!Z`Ybh5b_CtG%#Kaa-&8!OPkM=T(ON!1(kGBGq%=p+m0_fHOm z>O<24;c$6PK$HO{=Mc3j6v-=prJoo3xQIAt;YFnt73H^4ou;mF{z^tU+sihh0Ob)N z2-zJdX@NT>m7w^7GN@pf<3LhPtxOAmBhbAEYYnt~fD&?)g2ChN6Fx|FCQVb{5Em ztS3pYUjccI%4|hReI>c;8;$nG?yfDLe*Mrfeg5_Y&$c@KWrNELL z*JUQbB4D;Nqk6}9q)dyX`O5Njsly7l0jn;LR9RW0AtU<^yqC~+@#c1e3PIGWu}+Ev zmiOglhh(C$^SFXr$`0|WRbWVJh)u8-P=P#R z)9~hXy1A1Q?M_N2%EY$qi3xMx8Uu;uzt%x&fV2aNG7NHag8ub}!3$GoPn~g@M+l51 zd|&K?rD-&h7bvL&`i!L?+~Qt0CuhdeZw7=1nA^9#x9x-dJC?2;u&pC&8xmFY}%>HTQM&?*oUtF+J+MRKpoL#>#bfnr~t!4(47M(_4`e4}?K66KV(b#7^XMQh$7I?kkos0gzz^@s$9 z3|;qeP!R}b9X+XSp+^$ru~4F0ii{x`7r7iz~lj@gzofuu&+2#;s(`GVt&*5 z#goo$A8h^Hv3coR!rZv+ldXFloG4(MhL8P_>J3fb8?w+pqi7aQHZ=Y)@aK$vKc~a8 z8(B$Ee^W3lq*!@JC!eI9DUe6#lIfq5G5URDMoF&O)x0rt`L>m>tlar4Fpt5yhWb!M z*y^P;8p#X51Yov$DfuMr46r8QAU;P!y{i19szL%wl1lp%jSO6=tKrerAq=P?`Xfd42!(T$}-%*P0O%9oev6MQuFDICYeMA>bA_eTBAC~>Xt=oEebHjE#7Y{9w-A(;#34c1 zY7!Ssuwgx5P|#Hdh$-_8fQAV$(nhx46p!CIQWIsW`hwwTLqi~h{oFlP#^ zytZL3wp|xOJ(g+yD-D)y!>XaPtPeqeVkvkZwJEo4fj)A8EmWXQ!?pEINEG>jF#ImT zl6lLtsRhGT!5XNxOAK^hFqCO9Jq1=?TNlOWEQ`SWyNig6PEyNBfi*gfYd{p7iWqk$6RkTL){+k&)v)L&zd=Xf?)MyY&zyRz>opSX(k$-pj-su zXN`aA^uhu{Mn;lcy_Bf!J;_%>f+wcW=5I$dUA^>M-CrB3kJO3@A^~L$D6dP76TKc1FNNT9MEN^B6N_rB4u>cVN#ySiI>s>YR3RW4Znoxzf zvULxZqCJkyt?OHNZ|f*kmIACYQtPnTSl*VkkyK%Dgo;hW zIWt5}!OT=x{k%Yhpe`h9APH;ry7QJ<$Ogc$M%%J-5{9vn*J8o^)eX^5z|T{wA0%0s z#GSdclt!OCeZyau3WKLw@>vS(m2e2%QCQTtr06;uu^7eUF!We;%qVErc@8?Nz06el zljqqEgyoW~Tw8^o_0VWTXJVQ6(7jZ>JOD|3rXxfw<)LGc2*IKV+n9UwVxs`{rK%;p zd*CWGpLIyEuYj$APz-9hu?u>P)6o3Ld}UE&5`d9hUm|q|#Nx*D6XVbG^GqZ8BUaB^%q>NuXL5iC+) zE8;C+19C`L)4bOp<~za6H-v+Om>9cKmGvbTmA|d*SwY!Os_MqWs;t^e5Mmq zNJX?7zFEnRM0u1`XN0tB4x#`UYs{z(pUm90hp5Fy6E!nC&zz}m8qQr9Fjga_!OrLn z#;UNCyycR$2%UatN{xV;)Jm-=+}~38)p8`Nv{IyzGLHa9hlE3%{@tQNd}C zM_&s6$lS4I_eOLRG|k%Q<6*XY0G2o>5rNte)W|AXmfg8dXpv*FE}HAIUMUL0E?PMk zmdCkbMd4R1g@DPGh^hs>Qsoay@5*`Qjpe~wSqQTo$&Owd(%!HN#lO;o%`j+>VrPR| z^WID>5eJBs=)#1L?%$+yrHSOi$gpfisA0&jgd+`=4U2@W=}Ai}c@d6;D(hi1unsPQ zR01hxp45n$fGw;J&9_i;`5=R`lFwAikpM`g!>B4Giy&>*Aa)IUBy(J|@?WDd;Iql5 zpG4{s8>FuF?QRwLWa%fb+=cxllAbU*bR?zO;j9J8FogNHh!8+KJX$dCJ0Kjr% z6J$q9HJy`Yus-B3Z)m7pXawRpQay#p;6S(PIJ z1sh1PY%;WM7?~+rym8Z$j+4{|5X@~h4nS`~QLwc30^^EL83&YGix@5U0*0eL4x1X` zU=ix$_<=Ah(MC>{?MuyjsR)hPTqgr$o!HbEHzHZm#sM3>7=0_e0}0p*j7yrIHdzY4 zuc&xR;mpa!g3CbEZrmG>(>(YEX+Chi(fe5(u$jezV8^lT0Wc_lpxN^|FYo-U2wc!r zj&-Kb?<)qsl{^3=uZa@I!XTb&7CLud)$VT!)>f5=Y8r*~xwr+ZmGy7Ta?G>jM$>Y! zf1xz}H-h!0u1~i0@pux>k?t3zUb+hTXBJPHHgV?EtHk2$mC(r|QSfV+JV>)kzZVXP z9+_zLPbeyyTsVn=jfRhSa^wy3_z;OwSic(fM2`-@e3I8!Bh=jI0bRepJRA;$U_Fp? z5+^#)X3-^+nP>z{E=`XFL*pc?ChZI`xqK)th7v8wMX5S&yj(603r>l4lV4g=`vRcXfiXDP=sW|Ayn6L5*Pp z+RFyQ{<7uMEuSuZzfC9Dya3#Xl*$OOv26fu8XW1r-)m@*BeU9~M?6a~DacCQl0}UZ zJ*Kr?8KI;z4{9!0UQ2njVC-m=%$tO9?kpHMOr)CET0mYzPMJFiL+&a(27L3_r(`(#SObbyd>mIq$*v`fh+F-N_GqiHeN{3ZS zV`n^$>d`xnp}b)+LSxaCoteKC=oXCAN;1sA3_%__#IG@o+;_lBL%%L<;v2n{wV?pG z5KLr$hS7^%bkKW2gZP6OrocmM$Rh@@aAioq1%#LPk+_Mmm-Y|B-5$7@!HcLCqP>J6 zQNTttfD>lD`TcU`C0GSk-kK0Rj;h3P1-k_>*1~_V#Jvtsf&B}TJ3>BkjB>Q5S=Qm$SIS5&>yLkG-Pi+7m)em;HB3)2=;ockUs=wHv`k_lbY2GGSxm|b~7BF zn(V%+1R*&VoT;tQUe=XodMfMdz$XRD=R-VBQSEd~onT<2J>jPE267KUn_>aV!YmdU z#6~2n3edw@u=(Zl0#oYB35MZ}-J9s8O30!?oeG#G)xZAH-!ZfRR>^lz{qq>4!@I_WNkaPl19{9C!F{>9@42f5f+GK*x2JBxhA8hYIorCLG#77W9ezr4l{Mjtfi+5q3jj5%O;al7SqTe>+`&Uv_}+{SktIdVNoEnls@dD+%p(~5 zs7MH6*~ZG7zv8zsy&&UiXzSLl`M@0md#ET#=}d?iHr48H27-L|w5%@^6v`h-Ph#^dB=GD=j1* z^2QeUpEO7LZB$YccRKo|(e*={fB~O&k1m)Q$y0R67;>H$k%BSgEtK_6WFi%T2*M$^mg!j~?)GaZDk|sI)mI5$$*f#53W1$t zUp)m^)SAj`=Lu2;b)#Zkrsj>P^>NsV0uvDJi&1ZwPq0vqF|nvYgFJ4GiSiJPQ^b5i z9gbm`DRN0OpV$|2PHg^iz{+>hiU|pdVX8trR)t*HMYTY45lmGE{76Vo9-9JHDYQLF zhj?Wj!$)bjhob?Pa>cqgp#QKpfJ>GsT1VE8xdICaHL(lSB$8#5EUVxQ3P6*{Nykfl z5|pXD0gg~Of1v_W264>JDe4XBFSL@ypdZe#-qHoF2FPt9Yk%}qXa-2+p>;tH4OX!7 zA3q!J*iVwH*UEFZUkS5n&syNRfuKxaYDk)j0KfM>j+**N{lY~EB^H;99_LMOEzDfH zHlbrAf~6*K?t(1FjbQ0g_`Fq=noWSa4HejhQ7VGW4<8 zdqRO4?7RqsDr$n&_4XRwBZnz4a@+3lT-|`vSXC$xse_9oGbVlDBrF_75lFJnMc(Ah zuRJ_bsL`GYXrC~T^Gt5dVd(OPTn;XyU*fsf{LcCQbUA;@s^#t;t|f z3^OE&ZMImZICofMNm|ZbucWr_Fj$FybRO&r7R)A_e!{SY0s=qFwWT8?D)>pz>m^=W3A=n+8MA`{bD`FGC&`|cgin+vps_jSS zx|9h-%`!}a9s04DS_i1f$;p?A(GojbS#|tV>&vT(LiIH?+W^aXgW8qWL(@AD8#jqp zXd#mZj~OZ{v;qu+83LmT%0^&m(3I?9>%YK)#Zxp>Lq!>uM>dfn6$=?$NGo9-RMOZf z(3`Skey62*XIN86@Jb{T&5fPf1LiNDJYniY-%N{IAHT|4VW*lvceMVcyuz@CdfVng zkd(*PDZ@hH+6Z>Vc@Pt=7fJ+$QL%XPG168jf<>^=NjT5!$Q>Q`_cv2+9KXkxIA*slbY|dFFL7ev_ z2m?_=9wu1qa;$!G+2WU9TlZ(AmrTBGU1j+QVFgLSssz(48pE#5$by+VxkUEajDz*mxtfpJD*!>;Gl@cXg}g@<@FU zYZg@76ZCBT8){QoQr5cL3SYo#vBO&P;?u7G)~_QN++dPkiSa*)m*E}ezjWdxf6?@k zv(FOvX2D*9ksfJdfsTh8Gw4qNSY?Jyon8Q#9?(`&^#Zm!@1JLGy7<7Yc{kQ)#^bB= z-euU#>6gNrvbYq{=8`aY1!Z2SH}*T@4lG*=Hfi$2DbprQ<^6y@(>uuY4Man=HyS6e zja)H)ZNxL;QaBZlxN_RKDKlmkPo{cn-kXbwWoEen>Wrb{#) zDqclfkHyUCR-#mdR$?P0yOFdnNaL<3PEC{yd@$S5Oarwm?>+NZLfZ9%ImUKoxvh#f zg@G4<<<49@P_k*2bJ2GZr~<)}wT%d$j6}hfr7(oVmu+swzFop0GQth`;^^xHgV8BF zHWArVmP*HM9nQ+RyqP#PQEB!Iez@Ho}l1QMFAqlx-QsVcV}}7}Nw}0KU$7AGIm_IiUAoPl@z%*gyVa z=sxF1Ye`oM14%=zY^)J*!djiTs~{6Q0axe!V|r%02Sy_s6j<1;Z`+q1()T8Dk{b3C zqRZ{dSiSvKCc1h%^p;2~un>udkUGJv6j$$PW9*K$_A+`Lo=fB)Hb@b>dGFZOJHA-G ztL%XM*>~&e4S>~37!=kP>{w!>c{Z|IEj3Zv5zG}bCZ$J~macwh@qSRl(-8}*WQ|DJ zT-hyTA6mW3>t1ca(&`Pq6{)KJ0jAFrDX-%a$P!Cx4OU^H%s^3=-MX4!$!c4e2FtKf zt32Tap-2*ES9`Y;PdJ#CXt&4KCBc%!`fk&PdU%+ra-!+E-o~!ph3N&EM_b4f55rWg zSsrU;TvuqYcXzDbwfNAjt4#^(M(sR{$m`aLr?$T4?6d4-WuhZetCt&&U^d9g8y2Hv zD@a&iA$0fM5>2DVErK2*`%!9RohlnkwZi6GSFa*i4{8clkSSXWI8S+6qsk&owVHnQ zjy_YsN@`UqZ~XI*{-EiBp}R-k23oahH&!K-gAuVHBnQ1x zUO`83GPEpmymW3{Dxh^|Tq;aH#Uzg1>uOTEhEcHS3puJOZ%G*TlEvdkII(;%47@`F zzHRwS#0gD5T)O0}UD1)U2C0g9B4^*Wcg3ZMf|3G@WGPJo6j5j@rggmd0v|(cq(m)h zk!%Ue-u2MuSB)-!g67R?g$yC|ndO;Y8eJ-rJ;NVEpkp+hPB`PU|`dQHM#7ZIQ zu4jMm8+3AHCTbsugZcydQq>CJ2Fc7Hk{*0>gpi97v9s&-&qbUo91mge)aP+K!{E-fW z`jY&c?=Gdt3$=l|8u*y$r(oAJU|4qLLQi`avCxkwE_#+rK#MctV|lex?Y!kDNcCzh z&>e6-4TP|zTObheR($NC`>dd@tLwrIwa{T8QN;R@t|Yxc{RNX}dW-M{DE(Rl>zb(= z%Sw!lqDz-J#r6K_a9sgzmA+Z%`i-)OIAwnX7k5ayx9zFswu&GapQ-5x1=~x4bgNpK z3r|L*@>YDu4i!S3+L|DUA6((hDT13S8mma$qGMl&qlkNmYEx1RpR1@tbAiwW2eb$A zU8pm)Ln?3Frdf$7A)}lq{1+$`!fw$|iy#m=5(O(G`&v!}dvX9AiM&ZWpnkRXs#YSY zp@2DxO|?g0HT%hZs}I+vBjlPAd3dU6EpXncq~{hN_K64PL8V%)FW21SW-6)lxU)U; zO3AyXnl{x#VE;8jYn_J=>ky3MIiOZx3e-D(B8w{Lz>~GD+69PXxI~sTo4a?a%455^WK3}!*VREF;guagS2xHW&YnG-AGQV#O(UNJEG&@d zvzk-<1>a_2YH30W+wf|Mp`=8`Wr*+hicA#J2YXCHQh~iH$E+K`i%|d$1&u8D-N$k0g=eHt`ukOCwRRG+ADEG=tyh7{xfLkva(#`?&sFZJvC$=U)|Jgc8** z*5?s5pYpY3$I46+krrnYwP0Nhgy)0We&|GGNl|Ycgs98XIAYTTInjy-5uW>gh_XG? zlX;`*5}n*B2H<;!$5Mr)j(E!|8?BAhmqXiIf}m+m{m(sNIkYBk3>Xru3p+s z62XQ6Y(BX}LN{}0+_tW$B$$i1lk$@E9LUBm{s7J(R`z>IAL|8;o ztKY0W(`Vl1G4qC4u`lRP=~1dTsQP8I%E_kG+}`{xX|~}|j+U4pKGE<|a_VngBw$OA z$;QmwmDpwj80AfWL;t%r|FC&u@Ba_Pdi`r%&5d@>?UN=r0ne|%hkp>u?GEvazKYOP z873UKg5&V#3??Cl=HE6gw}RS+T^bU#6{88aNg3Ugp&KgvSz z@B9id1Oka_4?>OmFG-EL6XaCEgtS7(zSW_*|+K&*g z^7r6<0i(4eMQ~14egS1?6v>2j)tP-V3HM3gMmo6F}9mKDQ%f-9eiJ0N6yjcvgn<{$` zxoT7i{qMRD5gB*e5rD0ZVgH5#b>%Q)>R0Q}Lz&L3R7q~k2Tc>aO30K&1#!aeL}CTZ z2AF$aMhrwGO3ILQ^_4qI@;*-?=wZA57CssX7#Iu735pwpaz}Fr>2aH3N13L5!mWTc z0cJt=hi2mkl;D6h(ryzEKJ5D{j3iWV=pLk1BPm33`Oz#`1i+~B=>%LY^h?DI=(y~fN`dZu&ffH{Y$%8dz)a^Iz?P6<4_|Cg+%`fVXRrzo5eI*s3PX_ zBGsZ4{Gb!jX+f=d?|G1+$am`X*br*3BXie7+N~sZa2^7Wo{SIf_HhsltBm%U_6?#% zCu3B1gA)wPf_^1F7xJ{Ws@qfhl3=vT%+`LV&(p(wO@jmrH`F0GGCrV&4^-Iir=P%y z9X@>2F^52mJs?Z!L`B$!B_4_d~G zdp(GnoZ>35)^|}4VQ);!VM{57ZIrfCLv(W4rieMg4qLYJ7;&ybyq+5P@Z9y<@?KNK zO~7a>hbQy75EQKlwLQ=TgOwFb!D@YqzN4=q09HqB-wxsFMUGtBgA(0f7#j6l9Rb;O zd?T;-gJEdYo%;RyO?v0y66K@6 zba(&UF02i(DZ`*l+#41UMDTrA--s{GaNeL;> zEndJVYe=+6pNh!EfXVfnGY6C$tNtl7Ce4^Bkmv)DvkD02BlS@9bQt!r|9pH1LY~tV z`7w2nfH+aChtZ3Twq*NAFX*8b%a2QFbM=4ezae)o5JZ%M=5G>_t0j|xFW*ULFQMn? zr)X#CTWJpIfx)`aXlXih-LXi&jp~ioPgx8prC=lCo<^l~5Rez%)I4%V8lblBlb;l6 z3!aTPD$Xn)ftR0^5+zaNB3LBdd*Y39$)Xid+XR0CBx)r^bQq5J{C+9_H|iVEf5%ha zqy-9I0^_0c4M|2sp%C3LPmG0w1(lR`#M6dc&DxMVTu53JI(#xdA@P_l)ondGr+=YR z`)Ogc=~1%joxc42md1&hth%uixM^9!QiL3+h1&C-=ry%UzyfjD-`$(F&HL z6^yc4$6pPpg5@?tyFzd09s;2rvHJ$>9%=5tuI;X`evb-a7*NfowaK8*&z7qu3HzH*W z#ICO&3Tq7G$@lmAsru_wy`&)O7@J;o45JRLxe>GfLZXH&Nfw0~RjH5t6qk@isC&h6 z;;Lj3ur)oq^}Vjnjc79GZl=Z}R4KmE^%Nz_osB5TuEO*HlL8s!tXX=H^M;b9d)kH# zBnqa07#4hYUNa@EE0?P7?_=9w2Byl5CM1li_d;#0eiv6r7X=b*EpvC`TOpBtzr%cV zf`k#nlF-kU`Zr(6@||7zZRU1rGk7EuNeQFsy%V~mt+IM)Ub6s5Gk1BLgwdS;Eh3QT zs2+kCU+JC^3?mi;1Mp`$Pc_uvfqfHvl@f#!VF1rZC6yQs-XK1(dwaPFxW(o#t6UBW*jkh#{kxtE@ zf}!G;Au9PTcHX#3Wbe#1w~tjYRNPbIQG8w;)F6qgdLxb08`N6462?W<&y^ZlbaGWt z5@=D@=V{2Gq;=FUgZK=}wVWt+i>BtQ)Z9FtVayc#Og{@>VCNz@Ek7B%r!!131>cE> z6fBLH$9&b4Fk%Yw^h)TXWf7<+nQxP;x9W2YqpV@J`~^M@1DG5#)XPk5Hvk{h^1bZ*^@9Z1yH*dX?M#+N#JPBAipaZ7s>OygJHxJh!ga);j_ln=t-70 zGcs8kA>r@IFj~gNop2Nw#;w3(!F;h@eu6B|6)3XfH~ue^C+Sz=cLNtEhsjU`WVBS5l83log#rNFG+d=?BEX#ivOa8w6FNx`O2;qU8QF3L(}lYI1{CdNm}k{?}ktaIB#skY-*D z2F@dSCE1C}J!U;hihvUZr;c&YkeWOGa!MUO~3?rX(51gpa#zc+; zH6t%81s0eGdnH$|TWTj#U4yK83ooiek0KbQ2F*oYR>7c1e>%(GnrBnPCRJ?ClhhES z!^&AimLaX_u~Q4wP#G>~>VCkib&)jKh>B$Mq@VqN1B06{mDHEhxh@kQQ*3@+?>Ish zC9MeXSOv^xdXqKN?g^?f0|y&yYD}+&D{qPJb$b}c8 zmA7C+HCPCOQDezo>x8GRITTBh+lIk^u;&VRNwSlt_VMqnUJv}Vn&t*M(T?doS|TM; z@|0v4Z6Hcvl)!Uk^#Y@Pl-+^dVLg(QH@d^P-YB9G`S;okcXM~xe}~!--Uf9zYMdyP z3Ej&2A13<4^VTCp>@4_SPZZoBtJ8a}5OiH|tXA(O%YJGz*m+|Z-MEk&{!<5p`ApR{acj+)Lf-eogK6lC9iw!Z@<|{in99OzQZEDhMxR0zQivUX> z!i^9$HR3x9+&@#F#z-kC`1dM{1?31@Y027&lJ8GV-)p^9Z$A)54o5$L+V^nyR-)en z3kASGV@0a2Ew2gpT?AsXA;(bc5SrimI4I1BO$(nu;c*I~IO&ng?)U_QS8-5$f1+T^ zAyfv%F6fUBlW0RIP#Fvd>uq@fyX{gFrMN5gKa98hzlI%-b0BUdWvxf`COL=bG1NCn zg!XiU^;K_auqqx|NvUm~y=EyE$y!f&$hc|UOXJNv)eV*P4U1^s+K2@;HL~e-^3#8( z&RD%`2f6m+f?x)(AVmo$Z4 zLEfZR6PcI%BC4ag-GY4|gON;>BC7OJZ3vO_!xr%*wO&|)sfVfFWSd4doeAnwR82^?-GxSJ<)2P(?u#B<)^dC;SkY#YfpGLKD{uHPuXs zGB@0gS+91@8#WA#S$zYG*s7#FMMra$TyIqf!NNUZN5d&)fEZ>|Lu^ER1j8)RS6@akAdv`pBM3_DmCp3~0k>;!2y5A{S8! zSo34USoI^R!9$XsxBK(Re`o+KOi5}D6bp=Cgh(`~U^IywE^kVm&M=ubG(-AalR^zsJ~x(>h`3sYCj8l@rP%A<3yj8sYI=J-_XWC_l|h5IuVgx;d2J1(}f%>C~7pgOM=HJ0OB@J8(}2qnwSH z^R`g~)L4-VYRs-9pm@O`d`xU0ep6v7Y7=($+jtk(p8q7XF%^b6nu-AXNsq$z0)twZ z#dba1ysWxdg&pz3aeRduO}VqqX^obLj=bD-^Wp~0vR zdn$qvjM=nKZTlbA0 zVDj3W$1u4vBidk}G5u0ub?3{8OAZ$~)2mUaV8nic+F&+5COQ0bsMNeVQ%R*L2AS>r zKIJTeQM5jMarb-#4O8{Du^%9*C#k8uV{$2^QN73dnkvQI#g;BLoDu0YcHXAp6s{x_;0NOW4;h}ZH~;_u literal 0 HcmV?d00001 diff --git a/sigs/dccb3s.SIG b/sigs/dccb3s.SIG new file mode 100644 index 0000000000000000000000000000000000000000..65db78e517121ed567cc24a80a8cc5eb943d4ab4 GIT binary patch literal 59297 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 literal 0 HcmV?d00001 diff --git a/src/Command.cpp b/src/Command.cpp index 74c25d8..482bdad 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -8,7 +8,7 @@ bool LoaderSelection::execute(CommandContext * ctx) { - Project *proj=ctx->proj; + Project *proj=ctx->m_project; if(nullptr==proj) { ctx->recordFailure(this,"No active project "); return false; @@ -47,7 +47,7 @@ bool LoaderSelection::execute(CommandContext * ctx) bool LoaderApplication::execute(CommandContext * ctx) { - Project *proj=ctx->proj; + Project *proj=ctx->m_project; if(nullptr==proj) { ctx->recordFailure(this,"No active project "); @@ -96,6 +96,20 @@ void CommandStream::processAll(CommandContext *ctx) emit streamCompleted(true); } +bool CommandStream::processOne(CommandContext *ctx) +{ + if(not m_commands.isEmpty()) { + Command *cmd = m_commands.takeFirst(); + if(false==cmd->execute(ctx)) { + emit streamChanged(); + return false; + } + m_recently_executed.push_back(cmd); + } + emit streamChanged(); + return true; +} + void CommandStream::clear() { qDeleteAll(m_commands); diff --git a/src/Command.h b/src/Command.h index fe510f2..d04a921 100644 --- a/src/Command.h +++ b/src/Command.h @@ -21,7 +21,7 @@ public: m_failures.push_back({cmd,error_message}); } - Project *proj; + Project *m_project; QVector> m_failures; void reset(); }; @@ -60,10 +60,12 @@ public: QVector m_commands; bool add(Command *c); void setMaximumCommandCount(int maximum_command_count); + bool processOne(CommandContext *ctx); void processAll(CommandContext *ctx); void clear(); signals: void streamCompleted(bool success); + void streamChanged(); }; // Effect: loader has been selected and set in current project class LoaderSelection : public Command { diff --git a/src/DccFrontend.cpp b/src/DccFrontend.cpp index c5490c2..e528cc4 100644 --- a/src/DccFrontend.cpp +++ b/src/DccFrontend.cpp @@ -7,6 +7,7 @@ #include "disassem.h" #include "CallGraph.h" #include "Command.h" +#include "chklib.h" #include #include @@ -157,8 +158,8 @@ public: bool execute(CommandContext *ctx) override { - assert(ctx && ctx->proj); - Project &proj(*ctx->proj); + assert(ctx && ctx->m_project); + Project &proj(*ctx->m_project); const PROG &prog(proj.prog); proj.m_entry_state.setState(rES, 0); /* PSP segment */ proj.m_entry_state.setState(rDS, 0); @@ -180,7 +181,7 @@ struct CreateFunction : public Command { m_type(f) {} bool execute(CommandContext *ctx) override { - Project &proj(*ctx->proj); + Project &proj(*ctx->m_project); const PROG &prog(proj.prog); proj.createFunction(m_type,m_name,m_addr); @@ -201,15 +202,15 @@ struct FindMain : public Command { FindMain() : Command("Locate the main entry point",eProject) { } bool execute(CommandContext *ctx) { - Project &proj(*ctx->proj); + Project &proj(*ctx->m_project); const PROG &prog(proj.prog); - if(ctx->proj->m_entry_state.IP==0) { + if(ctx->m_project->m_entry_state.IP==0) { ctx->recordFailure(this,"Cannot search for main func when no entry point was found"); return false; } /* Check for special settings of initial state, based on idioms of the startup code */ - ctx->proj->m_entry_state.checkStartup(); + checkStartup(ctx->m_project->m_entry_state); Command *cmd; if (prog.offMain != -1) { @@ -227,29 +228,15 @@ struct FindMain : public Command { cmd = new CreateFunction("start",SegOffAddr {prog.segMain,proj.m_entry_state.IP},main_type); } proj.addCommand(cmd); + proj.addCommand(new LoadPatternLibrary()); return true; } }; -void DccFrontend::initializeMachineState(Project &proj) -{ - const PROG &prog(proj.prog); - proj.m_entry_state.setState(rES, 0); /* PSP segment */ - proj.m_entry_state.setState(rDS, 0); - proj.m_entry_state.setState(rCS, prog.initCS); - proj.m_entry_state.setState(rSS, prog.initSS); - proj.m_entry_state.setState(rSP, prog.initSP); - proj.m_entry_state.IP = ((uint32_t)prog.initCS << 4) + prog.initIP; - proj.SynthLab = SYNTHESIZED_MIN; -} /* Parses the program, builds the call graph, and returns the list of * procedures found */ void DccFrontend::parse(Project &proj) { - /* This proc needs to be called to set things up for LibCheck(), which - checks a proc to see if it is a know C (etc) library */ - proj.prog.bSigs = SetupLibCheck(); - /* Set initial state */ proj.addCommand(new MachineStateInitialization); proj.addCommand(new FindMain); diff --git a/src/backend.cpp b/src/backend.cpp index 7ba081d..fe8bb36 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -226,7 +226,7 @@ void Function::codeGen (QIODevice &fs) /* Write procedure/function header */ cCode.init(); - if (getReturnType() != TYPE_UNKNOWN) /* Function flg & PROC_IS_FUNC*/ + if (getReturnType() != TYPE_UNKNOWN) /* Function */ ostr << QString("\n%1 %2 (").arg(TypeContainer::typeName(getReturnType())).arg(name); else /* Procedure */ ostr << "\nvoid "+name+" ("; diff --git a/src/chklib.cpp b/src/chklib.cpp index a170dd9..641d449 100644 --- a/src/chklib.cpp +++ b/src/chklib.cpp @@ -4,7 +4,9 @@ * address of main() * (C) Mike van Emmerik */ +#include "chklib.h" +#include "Enums.h" #include "dcc.h" #include "msvc_fixes.h" #include "project.h" @@ -18,22 +20,21 @@ #include #include #include +#include "Command.h" -PerfectHash g_pattern_hasher; #define NIL -1 /* Used like NULL, but 0 is valid */ /* Hash table structure */ -typedef struct HT_tag +struct HT { char htSym[SYMLEN]; - uint8_t htPat[PATLEN]; -} HT; + uint8_t htPat[PATLEN]; +}; /* Structure of the prototypes table. Same as the struct in parsehdr.h, except here we don't need the "next" index (the elements are already sorted by function name) */ -typedef -struct ph_func_tag +struct PH_FUNC_STRUCT { char name[SYMLEN]; /* Name of function or arg */ hlType typ; /* Return type */ @@ -41,38 +42,24 @@ struct ph_func_tag int firstArg; /* Index of first arg in chain */ // int next; /* Index of next function in chain */ bool bVararg; /* True if variable arguements */ -} PH_FUNC_STRUCT; +} ; #define NUM_PLIST 64 /* Number of entries to increase allocation by */ /* statics */ static char buf[100]; /* A general purpose buffer */ -int numKeys; /* Number of hash table entries (keys) */ -int numVert; /* Number of vertices in the graph (also size of g[]) */ -unsigned PatLen; /* Size of the keys (pattern length) */ -unsigned SymLen; /* Max size of the symbols, including null */ -static FILE *g_file; /* File being read */ -static QString sSigName; /* Full path name of .sig file */ -static uint16_t *T1base, *T2base; /* Pointers to start of T1, T2 */ -static uint16_t *g; /* g[] */ -static HT *ht; /* The hash table */ -static PH_FUNC_STRUCT *pFunc; /* Points to the array of func names */ -static hlType *pArg=nullptr; /* Points to the array of param types */ -static int numFunc; /* Number of func names actually stored */ -static int numArg; /* Number of param names actually stored */ #define DCCLIBS "dcclibs.dat" /* Name of the prototypes data file */ /* prototypes */ -void grab(int n, FILE *_file); -uint16_t readFileShort(FILE *_file); -void readFileSection(uint16_t* p, int len, FILE *_file); -void cleanup(void); -void checkStartup(STATE *state); -void readProtoFile(void); -int searchPList(char *name); -void checkHeap(char *msg); /* For debugging */ +static bool grab(int n, QFile & _file); +static uint16_t readFileShort(FILE *_file); +static void readFileSection(uint16_t* p, int len, QFile & _file); +static void cleanup(void); +static void readProtoFile(void); +static int searchPList(char *name); +static void checkHeap(char *msg); /* For debugging */ void fixWildCards(uint8_t pat[]); /* In fixwild.c */ @@ -294,148 +281,10 @@ static uint8_t pattMsChkstk[] = 0xE9 /* jmp XXXX */ }; - - - -/* This procedure is called to initialise the library check code */ -bool SetupLibCheck(void) -{ - uint16_t w, len; - int i; - IDcc *dcc = IDcc::get(); - QString fpath = dcc->dataDir("sigs").absoluteFilePath(sSigName); - if ((g_file = fopen(qPrintable(fpath), "rb")) == nullptr) - { - printf("Warning: cannot open signature file %s\n", qPrintable(fpath)); - return false; - } - - readProtoFile(); - - - /* Read the parameters */ - grab(4, g_file); - if (memcmp("dccs", buf, 4) != 0) - { - printf("Not a dcc signature file!\n"); - exit(3); - } - numKeys = readFileShort(g_file); - numVert = readFileShort(g_file); - PatLen = readFileShort(g_file); - SymLen = readFileShort(g_file); - if ((PatLen != PATLEN) or (SymLen != SYMLEN)) - { - printf("Sorry! Compiled for sym and pattern lengths of %d and %d\n", SYMLEN, PATLEN); - return false; - } - - /* Initialise the perfhlib stuff. Also allocates T1, T2, g, etc */ - /* Set the parameters for the hash table */ - g_pattern_hasher.setHashParams( - numKeys, /* The number of symbols */ - PatLen, /* The length of the pattern to be hashed */ - 256, /* The character set of the pattern (0-FF) */ - 0, /* Minimum pattern character value */ - numVert); /* Specifies c, the sparseness of the graph. See Czech, Havas and Majewski for details */ - T1base = g_pattern_hasher.readT1(); - T2base = g_pattern_hasher.readT2(); - g = g_pattern_hasher.readG(); - - /* Read T1 and T2 tables */ - grab(2, g_file); - if (memcmp("T1", buf, 2) != 0) - { - printf("Expected 'T1'\n"); - exit(3); - } - len = (uint16_t) (PatLen * 256 * sizeof(uint16_t)); - w = readFileShort(g_file); - if (w != len) - { - printf("Problem with size of T1: file %d, calc %d\n", w, len); - return false; - } - readFileSection(T1base, len, g_file); - - grab(2, g_file); - if (memcmp("T2", buf, 2) != 0) - { - printf("Expected 'T2'\n"); - return false; - } - w = readFileShort(g_file); - if (w != len) - { - printf("Problem with size of T2: file %d, calc %d\n", w, len); - return false; - } - readFileSection(T2base, len, g_file); - - /* Now read the function g[] */ - grab(2, g_file); - if (memcmp("gg", buf, 2) != 0) - { - printf("Expected 'gg'\n"); - return false; - } - len = (uint16_t)(numVert * sizeof(uint16_t)); - w = readFileShort(g_file); - if (w != len) - { - printf("Problem with size of g[]: file %d, calc %d\n", w, len); - return false; - } - readFileSection(g, len, g_file); - - - /* This is now the hash table */ - /* First allocate space for the table */ - ht = new HT[numKeys]; - if ( nullptr == ht) - { - printf("Could not allocate hash table\n"); - return false; - } - grab(2, g_file); - if (memcmp("ht", buf, 2) != 0) - { - printf("Expected 'ht'\n"); - return false; - } - w = readFileShort(g_file); - if (w != numKeys * (SymLen + PatLen + sizeof(uint16_t))) - { - printf("Problem with size of hash table: file %d, calc %d\n", w, len); - return false; - } - - - for (i=0; i < numKeys; i++) - { - if (fread(&ht[i], 1, SymLen + PatLen, g_file) != SymLen + PatLen) - { - printf("Could not read signature\n"); - return false; - } - } - fclose(g_file); - return true; -} - - -void CleanupLibCheck(void) -{ - /* Deallocate all the stuff allocated in SetupLibCheck() */ - delete [] ht; - delete [] pFunc; -} - - /* Check this function to see if it is a library function. Return true if it is, and copy its name to pProc->name */ -bool LibCheck(Function & pProc) +bool PatternLocator::LibCheck(Function & pProc) { PROG &prog(Project::get()->prog); long fileOffset; @@ -443,13 +292,6 @@ bool LibCheck(Function & pProc) int Idx; uint8_t pat[PATLEN]; - if (prog.bSigs == false) - { - /* No signatures... can't rely on hash parameters to be initialised - so always return false */ - return false; - } - fileOffset = pProc.procEntry; /* Offset into the image */ if (fileOffset == prog.offMain) { @@ -545,35 +387,29 @@ bool LibCheck(Function & pProc) -void grab(int n, FILE *_file) +bool grab(int n, QFile &_file) { - if (fread(buf, 1, n, _file) != (unsigned)n) + + if (_file.read(buf,n) != n) { - printf("Could not grab\n"); - exit(11); + qCritical() << "File read failed"; + return false; } + return true; } -uint16_t -readFileShort(FILE *f) +uint16_t readFileShort(QFile &f) { - uint8_t b1, b2; - - if (fread(&b1, 1, 1, f) != 1) - { - printf("Could not read short\n"); - exit(11); + uint16_t tgt; + if(sizeof(tgt)!=f.read((char *)&tgt,sizeof(tgt))) { + qCritical() << "File read failed"; + return false; } - if (fread(&b2, 1, 1, f) != 1) - { - printf("Could not read short\n"); - exit(11); - } - return (uint16_t)(b2 << 8) + (uint16_t)b1; + return tgt; } // Read a section of the file, considering endian issues -void readFileSection(uint16_t* p, int len, FILE* f) +static void readFileSection(uint16_t* p, int len, QFile & f) { for (int i=0; i < len; i += 2) { @@ -581,23 +417,11 @@ void readFileSection(uint16_t* p, int len, FILE* f) } } -/* The following two functions are dummies, since we don't call map() */ -void getKey(int /*i*/, uint8_t **/*keys*/) -{ - -} - -void dispKey(int /*i*/) -{ - -} - /* Search the source array between limits iMin and iMax for the pattern (length iPatLen). The pattern can contain wild bytes; if you really want to match for the pattern that is used up by the WILD uint8_t, tough - it will match with everything else as well. */ -static bool locatePattern(const uint8_t *source, int iMin, int iMax, uint8_t *pattern, int iPatLen, - int *index) +static bool locatePattern(const uint8_t *source, int iMin, int iMax, uint8_t *pattern, int iPatLen, int *index) { int i, j; const uint8_t *pSrc; /* Pointer to start of considered source */ @@ -633,26 +457,25 @@ static bool locatePattern(const uint8_t *source, int iMin, int iMax, uint8_t *pa } -void STATE::checkStartup() +/** + * This function checks the startup code for various compilers' way of + * loading DS. If found, it sets DS. This may not be needed in the future if + * pushing and popping of registers is implemented. + * Also sets prog.offMain and prog.segMain if possible + */ +void checkStartup(STATE &state) { - PROG &prog(Project::get()->prog); - /* This function checks the startup code for various compilers' way of - loading DS. If found, it sets DS. This may not be needed in the future if - pushing and popping of registers is implemented. - Also sets prog.offMain and prog.segMain if possible */ + Project & proj(*Project::get()); + PROG & prog(Project::get()->prog); int startOff; /* Offset into the Image of the initial CS:IP */ int i, rel, para, init; - char chModel = 'x'; - char chVendor = 'x'; - char chVersion = 'x'; - char temp[4]; startOff = ((uint32_t)prog.initCS << 4) + prog.initIP; /* Check the Turbo Pascal signatures first, since they involve only the - first 3 bytes, and false positives may be founf with the others later */ + first 3 bytes, and false positives may be found with the others later */ if (locatePattern(prog.image(), startOff, startOff+5, pattBorl4on,sizeof(pattBorl4on), &i)) { /* The first 5 bytes are a far call. Follow that call and @@ -660,49 +483,41 @@ void STATE::checkStartup() rel = LH(&prog.image()[startOff+1]); /* This is abs off of init */ para= LH(&prog.image()[startOff+3]);/* This is abs seg of init */ init = ((uint32_t)para << 4) + rel; - if (locatePattern(prog.image(), init, init+26, pattBorl4Init, - sizeof(pattBorl4Init), &i)) + if (locatePattern(prog.image(), init, init+26, pattBorl4Init, sizeof(pattBorl4Init), &i)) { - setState(rDS, LH(&prog.image()[i+1])); - printf("Borland Pascal v4 detected\n"); - chVendor = 't'; /* Trubo */ - chModel = 'p'; /* Pascal */ - chVersion = '4'; /* Version 4 */ + state.setState(rDS, LH(&prog.image()[i+1])); + printf("Borland Turbo Pascal v4 detected\n"); + proj.setLoaderMetadata({eBorland,ePascal,eUnknownMemoryModel,4}); prog.offMain = startOff; /* Code starts immediately */ prog.segMain = prog.initCS; /* At the 5 uint8_t jump */ - goto gotVendor; /* Already have vendor */ + return; /* Already have vendor */ } - else if (locatePattern(prog.image(), init, init+26, pattBorl5Init, - sizeof(pattBorl5Init), &i)) + else if (locatePattern(prog.image(), init, init+26, pattBorl5Init, sizeof(pattBorl5Init), &i)) { - setState( rDS, LH(&prog.image()[i+1])); - printf("Borland Pascal v5.0 detected\n"); - chVendor = 't'; /* Trubo */ - chModel = 'p'; /* Pascal */ - chVersion = '5'; /* Version 5 */ + state.setState( rDS, LH(&prog.image()[i+1])); + printf("Borland Turbo Pascal v5.0 detected\n"); + proj.setLoaderMetadata({eBorland,ePascal,eUnknownMemoryModel,5}); prog.offMain = startOff; /* Code starts immediately */ prog.segMain = prog.initCS; - goto gotVendor; /* Already have vendor */ + return; /* Already have vendor */ } else if (locatePattern(prog.image(), init, init+26, pattBorl7Init, sizeof(pattBorl7Init), &i)) { - setState( rDS, LH(&prog.image()[i+1])); + state.setState( rDS, LH(&prog.image()[i+1])); printf("Borland Pascal v7 detected\n"); - chVendor = 't'; /* Trubo */ - chModel = 'p'; /* Pascal */ - chVersion = '7'; /* Version 7 */ + proj.setLoaderMetadata({eBorland,ePascal,eUnknownMemoryModel,7}); prog.offMain = startOff; /* Code starts immediately */ prog.segMain = prog.initCS; - goto gotVendor; /* Already have vendor */ + return; /* Already have vendor */ } - } + LoaderMetadata metadata; /* Search for the call to main pattern. This is compiler independant, but decides the model required. Note: must do the far data models (large and compact) before the others, since they are the same pattern @@ -716,7 +531,7 @@ void STATE::checkStartup() /* Save absolute image offset */ prog.offMain = ((uint32_t)para << 4) + rel; prog.segMain = (uint16_t)para; - chModel = 'l'; /* Large model */ + metadata.compiler_memory_model = eLarge; } else if (locatePattern(prog.image(), startOff, startOff+0x180, pattMainCompact, sizeof(pattMainCompact), &i)) @@ -724,7 +539,7 @@ void STATE::checkStartup() rel = LH_SIGNED(&prog.image()[i+OFFMAINCOMPACT]);/* This is the rel addr of main */ prog.offMain = i+OFFMAINCOMPACT+2+rel; /* Save absolute image offset */ prog.segMain = prog.initCS; - chModel = 'c'; /* Compact model */ + metadata.compiler_memory_model = eCompact; } else if (locatePattern(prog.image(), startOff, startOff+0x180, pattMainMedium, sizeof(pattMainMedium), &i)) @@ -733,7 +548,7 @@ void STATE::checkStartup() para= LH(&prog.image()[i+OFFMAINMEDIUM+2]);/* This is abs seg of main */ prog.offMain = ((uint32_t)para << 4) + rel; prog.segMain = (uint16_t)para; - chModel = 'm'; /* Medium model */ + metadata.compiler_memory_model = eMedium; } else if (locatePattern(prog.image(), startOff, startOff+0x180, pattMainSmall, sizeof(pattMainSmall), &i)) @@ -741,7 +556,7 @@ void STATE::checkStartup() rel = LH_SIGNED(&prog.image()[i+OFFMAINSMALL]); /* This is rel addr of main */ prog.offMain = i+OFFMAINSMALL+2+rel; /* Save absolute image offset */ prog.segMain = prog.initCS; - chModel = 's'; /* Small model */ + metadata.compiler_memory_model = eSmall; } else if (memcmp(&prog.image()[startOff], pattTPasStart, sizeof(pattTPasStart)) == 0) { @@ -749,12 +564,10 @@ void STATE::checkStartup() prog.offMain = rel+startOff+3; /* Save absolute image offset */ prog.offMain += 0x20; /* These first 32 bytes are setting up */ prog.segMain = prog.initCS; - chVendor = 't'; /* Turbo.. */ - chModel = 'p'; /* ...Pascal... (only 1 model) */ - chVersion = '3'; /* 3.0 */ + proj.setLoaderMetadata({eBorland,ePascal,eUnknownMemoryModel,3}); printf("Turbo Pascal 3.0 detected\n"); printf("Main at %04X\n", prog.offMain); - goto gotVendor; /* Already have vendor */ + return; /* Already have vendor */ } else { @@ -767,28 +580,28 @@ void STATE::checkStartup() printf("Main could not be located!\n"); prog.offMain = -1; } - - printf("Model: %c\n", chModel); - prog.addressingMode = chModel; + //printf("Model: %c\n", chModel); /* Now decide the compiler vendor and version number */ if (memcmp(&prog.image()[startOff], pattMsC5Start, sizeof(pattMsC5Start)) == 0) { /* Yes, this is Microsoft startup code. The DS is sitting right here in the next 2 bytes */ - setState( rDS, LH(&prog.image()[startOff+sizeof(pattMsC5Start)])); - chVendor = 'm'; /* Microsoft compiler */ - chVersion = '5'; /* Version 5 */ + state.setState( rDS, LH(&prog.image()[startOff+sizeof(pattMsC5Start)])); + metadata.compiler_vendor = eMicrosoft; + metadata.compiler_language = eAnsiCorCPP; + metadata.compiler_version = 5; printf("MSC 5 detected\n"); } /* The C8 startup pattern is different from C5's */ else if (memcmp(&prog.image()[startOff], pattMsC8Start, sizeof(pattMsC8Start)) == 0) { - setState( rDS, LH(&prog.image()[startOff+sizeof(pattMsC8Start)])); + state.setState( rDS, LH(&prog.image()[startOff+sizeof(pattMsC8Start)])); printf("MSC 8 detected\n"); - chVendor = 'm'; /* Microsoft compiler */ - chVersion = '8'; /* Version 8 */ + metadata.compiler_vendor = eMicrosoft; + metadata.compiler_language = eAnsiCorCPP; + metadata.compiler_version = 8; } /* The C8 .com startup pattern is different again! */ @@ -796,37 +609,35 @@ void STATE::checkStartup() sizeof(pattMsC8ComStart)) == 0) { printf("MSC 8 .com detected\n"); - chVendor = 'm'; /* Microsoft compiler */ - chVersion = '8'; /* Version 8 */ + metadata.compiler_vendor = eMicrosoft; + metadata.compiler_language = eAnsiCorCPP; + metadata.compiler_version = 8; } - - else if (locatePattern(prog.image(), startOff, startOff+0x30, pattBorl2Start, - sizeof(pattBorl2Start), &i)) + else if (locatePattern(prog.image(), startOff, startOff+0x30, pattBorl2Start, sizeof(pattBorl2Start), &i)) { /* Borland startup. DS is at the second uint8_t (offset 1) */ - setState( rDS, LH(&prog.image()[i+1])); + state.setState( rDS, LH(&prog.image()[i+1])); printf("Borland v2 detected\n"); - chVendor = 'b'; /* Borland compiler */ - chVersion = '2'; /* Version 2 */ + metadata.compiler_vendor = eBorland; + metadata.compiler_language = eAnsiCorCPP; + metadata.compiler_version = 2; } - - else if (locatePattern(prog.image(), startOff, startOff+0x30, pattBorl3Start, - sizeof(pattBorl3Start), &i)) + else if (locatePattern(prog.image(), startOff, startOff+0x30, pattBorl3Start, sizeof(pattBorl3Start), &i)) { /* Borland startup. DS is at the second uint8_t (offset 1) */ - setState( rDS, LH(&prog.image()[i+1])); + state.setState( rDS, LH(&prog.image()[i+1])); printf("Borland v3 detected\n"); - chVendor = 'b'; /* Borland compiler */ - chVersion = '3'; /* Version 3 */ + metadata.compiler_vendor = eBorland; + metadata.compiler_language = eAnsiCorCPP; + metadata.compiler_version = 3; } - - else if (locatePattern(prog.image(), startOff, startOff+0x30, pattLogiStart, - sizeof(pattLogiStart), &i)) + else if (locatePattern(prog.image(), startOff, startOff+0x30, pattLogiStart, sizeof(pattLogiStart), &i)) { /* Logitech modula startup. DS is 0, despite appearances */ printf("Logitech modula detected\n"); - chVendor = 'l'; /* Logitech compiler */ - chVersion = '1'; /* Version 1 */ + metadata.compiler_vendor = eLogitech; + metadata.compiler_language = eModula2; + metadata.compiler_version = 1; } /* Other startup idioms would go here */ @@ -834,16 +645,7 @@ void STATE::checkStartup() { printf("Warning - compiler not recognised\n"); } - -gotVendor: - - sSigName = QString("dcc%1%2%3.sig") - .arg(QChar(chVendor)) /* Add vendor */ - .arg(QChar(chVersion)) /* Add version */ - .arg(QChar(chModel)) /* Add model */ - ; - printf("Signature file: %s\n", qPrintable(sSigName)); - + proj.setLoaderMetadata(metadata); } /* DCCLIBS.DAT is a data file sorted on function name containing names and @@ -853,32 +655,31 @@ gotVendor: by dcc, rather than considered as known functions. When a prototype is found (in searchPList()), the parameter info is written to the proc struct. */ -void readProtoFile(void) +bool PatternLocator::readProtoFile(void) { IDcc *dcc = IDcc::get(); QString szProFName = dcc->dataDir("prototypes").absoluteFilePath(DCCLIBS); /* Full name of dclibs.lst */ - FILE *fProto; - int i; + QFile fProto(szProFName); - if ((fProto = fopen(qPrintable(szProFName), "rb")) == nullptr) + if (not fProto.open(QFile::ReadOnly)) { printf("Warning: cannot open library prototype data file %s\n", qPrintable(szProFName)); - return; + return false; } grab(4, fProto); if (strncmp(buf, "dccp", 4) != 0) { printf("%s is not a dcc prototype file\n", qPrintable(szProFName)); - exit(1); + return false; } grab(2, fProto); if (strncmp(buf, "FN", 2) != 0) { printf("FN (Function Name) subsection expected in %s\n", qPrintable(szProFName)); - exit(2); + return false; } numFunc = readFileShort(fProto); /* Num of entries to allocate */ @@ -886,51 +687,48 @@ void readProtoFile(void) /* Allocate exactly correct # entries */ pFunc = new PH_FUNC_STRUCT[numFunc]; - for (i=0; i < numFunc; i++) + for (int i=0; i < numFunc; i++) { - size_t read_size=fread(&pFunc[i], 1, SYMLEN, fProto); + size_t read_size=fProto.read((char *)&pFunc[i], SYMLEN); assert(read_size==SYMLEN); if(read_size!=SYMLEN) break; pFunc[i].typ = (hlType)readFileShort(fProto); pFunc[i].numArg = readFileShort(fProto); pFunc[i].firstArg = readFileShort(fProto); - if(feof(fProto)) + if(fProto.atEnd()) break; - int c = fgetc(fProto); - pFunc[i].bVararg = (c!=0); //fread(&pFunc[i].bVararg, 1, 1, fProto); + char isvararg; + fProto.read(&isvararg,1); + pFunc[i].bVararg = (isvararg!=0); } grab(2, fProto); if (strncmp(buf, "PM", 2) != 0) { printf("PM (Parameter) subsection expected in %s\n", qPrintable(szProFName)); - exit(2); + return false; } numArg = readFileShort(fProto); /* Num of entries to allocate */ /* Allocate exactly correct # entries */ - delete [] pArg; - pArg = new hlType[numArg]; + pArg.clear(); + pArg.reserve(numArg); - for (i=0; i < numArg; i++) + for (int i=0; i < numArg; i++) { // fread(&pArg[i], 1, SYMLEN, fProto); /* No names to read as yet */ - pArg[i] = (hlType) readFileShort(fProto); + pArg.push_back((hlType) readFileShort(fProto)); } - - fclose(fProto); - } -int searchPList(char *name) +int PatternLocator::searchPList(const char *name) { /* Search through the symbol names for the name */ /* Use binary search */ int mx, mn, i, res; - mx = numFunc; mn = 0; @@ -939,29 +737,173 @@ int searchPList(char *name) i = (mn + mx) /2; res = strcmp(pFunc[i].name, name); if (res == 0) - { return i; /* Found! */ - } + + if (res < 0) + mn = i+1; else - { - if (res < 0) - { - mn = i+1; - } - else - { - mx = i-1; - } - } + mx = i-1; } /* Still could be the case that mn == mx == required record */ res = strcmp(pFunc[mn].name, name); if (res == 0) - { return mn; /* Found! */ - } + return NIL; } +/* This procedure is called to initialise the library check code */ +bool PatternLocator::load() +{ + uint16_t w, len; + int i; + IDcc *dcc = IDcc::get(); + QString fpath = dcc->dataDir("sigs").absoluteFilePath(pattern_id); + QFile g_file(fpath); + if (not g_file.open(QFile::ReadOnly)) + { + printf("Warning: cannot open signature file %s\n", qPrintable(fpath)); + return false; + } + + readProtoFile(); + /* Read the parameters */ + grab(4, g_file); + if (memcmp("dccs", buf, 4) != 0) + { + printf("Not a dcc signature file!\n"); + return false; + } + numKeys = readFileShort(g_file); + numVert = readFileShort(g_file); + PatLen = readFileShort(g_file); + SymLen = readFileShort(g_file); + if ((PatLen != PATLEN) or (SymLen != SYMLEN)) + { + printf("Sorry! Compiled for sym and pattern lengths of %d and %d\n", SYMLEN, PATLEN); + return false; + } + + /* Initialise the perfhlib stuff. Also allocates T1, T2, g, etc */ + /* Set the parameters for the hash table */ + g_pattern_hasher.setHashParams( + numKeys, /* The number of symbols */ + PatLen, /* The length of the pattern to be hashed */ + 256, /* The character set of the pattern (0-FF) */ + 0, /* Minimum pattern character value */ + numVert); /* Specifies c, the sparseness of the graph. See Czech, Havas and Majewski for details */ + T1base = g_pattern_hasher.readT1(); + T2base = g_pattern_hasher.readT2(); + g = g_pattern_hasher.readG(); + + /* Read T1 and T2 tables */ + grab(2, g_file); + if (memcmp("T1", buf, 2) != 0) + { + printf("Expected 'T1'\n"); + exit(3); + } + len = (uint16_t) (PatLen * 256 * sizeof(uint16_t)); + w = readFileShort(g_file); + if (w != len) + { + printf("Problem with size of T1: file %d, calc %d\n", w, len); + return false; + } + readFileSection(T1base, len, g_file); + + grab(2, g_file); + if (memcmp("T2", buf, 2) != 0) + { + printf("Expected 'T2'\n"); + return false; + } + w = readFileShort(g_file); + if (w != len) + { + printf("Problem with size of T2: file %d, calc %d\n", w, len); + return false; + } + readFileSection(T2base, len, g_file); + + /* Now read the function g[] */ + grab(2, g_file); + if (memcmp("gg", buf, 2) != 0) + { + printf("Expected 'gg'\n"); + return false; + } + len = (uint16_t)(numVert * sizeof(uint16_t)); + w = readFileShort(g_file); + if (w != len) + { + printf("Problem with size of g[]: file %d, calc %d\n", w, len); + return false; + } + readFileSection(g, len, g_file); + + + /* This is now the hash table */ + /* First allocate space for the table */ + ht = new HT[numKeys]; + if ( nullptr == ht) + { + printf("Could not allocate hash table\n"); + return false; + } + grab(2, g_file); + if (memcmp("ht", buf, 2) != 0) + { + printf("Expected 'ht'\n"); + return false; + } + w = readFileShort(g_file); + if (w != numKeys * (SymLen + PatLen + sizeof(uint16_t))) + { + printf("Problem with size of hash table: file %d, calc %d\n", w, len); + return false; + } + + + for (i=0; i < numKeys; i++) + { + if (g_file.read((char *)&ht[i], SymLen + PatLen) != SymLen + PatLen) + { + printf("Could not read signature\n"); + return false; + } + } + return true; +} +PatternLocator::~PatternLocator() +{ + /* Deallocate all the stuff allocated in SetupLibCheck() */ + delete [] ht; + delete [] pFunc; + delete [] T1base; + delete [] T2base; + delete [] g; +} + +bool LoadPatternLibrary::execute(CommandContext * ctx) +{ + if(nullptr==ctx->m_project) { + ctx->recordFailure(this,"Project was not created yet."); + return false; + } + Project & project( *ctx->m_project) ; + if(!project.m_metadata_available) { + ctx->recordFailure(this,"Loader metadata was not set. FindMain command should be run before LoadPatternLibrary"); + return false; + } + PatternLocator *loc = new PatternLocator(project.getLoaderMetadata().compilerId()); + if(!loc->load()) { + qDebug()<< " No library patterns found for" << project.getLoaderMetadata().compilerId(); + delete loc; + return true; // not an error, just no pattern locator available + } + project.m_pattern_locator = loc; + return true; +} diff --git a/src/chklib.h b/src/chklib.h new file mode 100644 index 0000000..b261893 --- /dev/null +++ b/src/chklib.h @@ -0,0 +1,49 @@ +#ifndef CHKLIB_H +#define CHKLIB_H + +#include "Command.h" +#include "Enums.h" +#include "perfhlib.h" + +#include +#include +#include + +struct Function; + +// This will create a PatternLocator instance load it and pass it to project instance. +struct LoadPatternLibrary : public Command { + LoadPatternLibrary() : Command("Load patterns for the file",eProject) {} + bool execute(CommandContext *ctx) override; +}; + +class PatternLocator { + std::vector pArg; /* Points to the array of param types */ + QString pattern_id; + int numFunc; /* Number of func names actually stored */ + int numArg; /* Number of param names actually stored */ +public: + struct HT *ht; //!< The hash table + struct PH_FUNC_STRUCT *pFunc; //!< Points to the array of func names + + + PatternLocator(QString name) : pattern_id(name) {} + ~PatternLocator(); + + bool load(); + int searchPList(const char * name); + + bool LibCheck(Function & pProc); +private: + bool readProtoFile(); + PerfectHash g_pattern_hasher; + int numKeys; /* Number of hash table entries (keys) */ + int numVert; /* Number of vertices in the graph (also size of g[]) */ + unsigned PatLen; /* Size of the keys (pattern length) */ + unsigned SymLen; /* Max size of the symbols, including null */ + uint16_t * T1base, *T2base; /* Pointers to start of T1, T2 */ + uint16_t * g; /* g[] */ + +}; +extern void checkStartup(struct STATE &state); +#endif // CHKLIB_H diff --git a/src/dataflow.cpp b/src/dataflow.cpp index c4711a9..2dfaf7b 100644 --- a/src/dataflow.cpp +++ b/src/dataflow.cpp @@ -671,9 +671,7 @@ int C_CallingConvention::processCArg (Function * callee, Function * pProc, ICODE Expr *_exp; bool res; int size_of_arg=0; - PROG &prog(Project::get()->prog); - - + Project &proj(*Project::get()); /* if (numArgs == 0) return; */ assert(pProc==g_exp_stk.func); @@ -693,7 +691,7 @@ int C_CallingConvention::processCArg (Function * callee, Function * pProc, ICODE } else { if(numArgsargs.size()) { - if(prog.addressingMode=='l') { + if(proj.getLoaderMetadata().compiler_memory_model==eLarge) { if((callee->args[numArgs].type==TYPE_STR) or (callee->args[numArgs].type==TYPE_PTR)) { RegisterNode *rn = dynamic_cast(g_exp_stk.top()); AstIdent *idn = dynamic_cast(g_exp_stk.top()); diff --git a/src/dcc.cpp b/src/dcc.cpp index 270b599..2172006 100644 --- a/src/dcc.cpp +++ b/src/dcc.cpp @@ -14,26 +14,10 @@ #include #include #include +#include #include - -#ifdef LLVM_EXPERIMENTAL -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif #include - +#include "ui/DccMainWindow.h" /* Global variables - extern to other modules */ extern QString asm1_name, asm2_name; /* Assembler output filenames */ @@ -46,79 +30,6 @@ static void displayTotalStats(void); /**************************************************************************** * main ***************************************************************************/ -#ifdef LLVM_EXPERIMENTAL -using namespace llvm; -bool TVisitor(raw_ostream &OS, RecordKeeper &Records) -{ - Record *rec = Records.getDef("ADD8i8"); - if(rec) - { - if(not rec->getTemplateArgs().empty()) - std::cout << "Has template args\n"; - auto classes(rec->getSuperClasses()); - for(auto val : rec->getSuperClasses()) - std::cout << "Super "<getName()<<"\n"; - - // DagInit * in = rec->getValueAsDag(val.getName()); - // in->dump(); - for(const RecordVal &val : rec->getValues()) - { - // val.dump(); - } - rec->dump(); - - } - // rec = Records.getDef("CCR"); - // if(rec) - // rec->dump(); -// for(auto val : Records.getDefs()) -// { -// //std::cout<< "Def "<createTargetMachine(TheTriple.getTriple(),MCPU,Features,opts); -// std::cerr<getInstrInfo()->getName(97)<<"\n"; -// const MCInstrDesc &ds(tm->getInstrInfo()->get(97)); -// const MCOperandInfo *op1=ds.OpInfo; -// uint16_t impl_def = ds.getImplicitDefs()[0]; -// std::cerr<functions().begin(); } + ilFunction GetNextFuncHandle(ilFunction iter) + { + if(iter!=Project::get()->functions().end()) + ++iter; + return iter; + } ilFunction GetCurFuncHandle() { return m_current_func; } void analysis_Once() { + if(m_current_func==Project::get()->functions().end()) + return; + if(m_current_func->nStep==0) { // unscanned function + } } - void load(QString name) + bool load(QString name) { option.filename = name; Project::get()->create(name); + return Project::get()->addLoadCommands(name); } - void prtout_asm(IXmlTarget *, int level) + void prtout_asm(IStructuredTextTarget *, int level) + { +// if (m_Cur_Func->m_nStep == 0) +// return; + +// XmlOutPro out(iOut); +// FuncLL the(m_Cur_Func->ll.m_asmlist); +// the.prtout_asm(m_Cur_Func, &m_Cur_Func->m_varll, &out); + } + void prtout_cpp(IStructuredTextTarget *, int level) { } - void prtout_cpp(IXmlTarget *, int level) - { + bool isValidFuncHandle(ilFunction f) { + return f != Project::get()->functions().end(); } size_t getFuncCount() { diff --git a/src/parser.cpp b/src/parser.cpp index 56ae980..9920933 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6,6 +6,7 @@ #include "project.h" #include "CallGraph.h" #include "msvc_fixes.h" +#include "chklib.h" #include #include @@ -673,6 +674,7 @@ bool Function::process_JMP (ICODE & pIcode, STATE *pstate, CALL_GRAPH * pcallGra bool Function::process_CALL(ICODE & pIcode, CALL_GRAPH * pcallGraph, STATE *pstate) { + Project &project(*Project::get()); PROG &prog(Project::get()->prog); ICODE &last_insn(Icode.back()); STATE localState; /* Local copy of the machine state */ @@ -742,7 +744,8 @@ bool Function::process_CALL(ICODE & pIcode, CALL_GRAPH * pcallGraph, STATE *psta { iter = Project::get()->createFunction(0,"",{0,pIcode.ll()->src().getImm2()}); Function &x(*iter); - LibCheck(x); + if(project.m_pattern_locator) + project.m_pattern_locator->LibCheck(x); if (x.flg & PROC_ISLIB) { diff --git a/src/project.cpp b/src/project.cpp index 1eebf33..79bec21 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -15,7 +15,10 @@ STATS stats; /* cfg statistics */ //PROG prog; /* programs fields */ OPTION option; /* Command line options */ Project *Project::s_instance = nullptr; -Project::Project() : callGraph(nullptr) +Project::Project() : + m_selected_loader(nullptr), + callGraph(nullptr), + m_pattern_locator(nullptr) { m_project_command_stream.setMaximumCommandCount(10); connect(&m_project_command_stream,SIGNAL(streamCompleted(bool)),SLOT(onCommandStreamFinished(bool))); @@ -29,6 +32,7 @@ void Project::initialize() } void Project::create(const QString &a) { + // TODO: reset all state. initialize(); QFileInfo fi(a); m_fname=a; @@ -152,15 +156,23 @@ bool Project::addLoadCommands(QString fname) void Project::processAllCommands() { - m_command_ctx.proj = this; + m_command_ctx.m_project = this; m_project_command_stream.processAll(&m_command_ctx); - + emit commandListChanged(); +} +void Project::processCommands(int count) { + m_command_ctx.m_project = this; + while(count--) { + if(false==m_project_command_stream.processOne(&m_command_ctx)) { + break; + } + } + emit commandListChanged(); } - void Project::resetCommandsAndErrorState() { m_error_state = false; m_command_ctx.reset(); - m_command_ctx.proj = this; + m_command_ctx.m_project = this; m_project_command_stream.clear(); } diff --git a/src/ui/CommandQueueView.cpp b/src/ui/CommandQueueView.cpp new file mode 100644 index 0000000..1f195a9 --- /dev/null +++ b/src/ui/CommandQueueView.cpp @@ -0,0 +1,39 @@ +#include "CommandQueueView.h" + +#include "project.h" + +#include "ui_CommandQueueView.h" + +CommandQueueView::CommandQueueView(QWidget *parent) : + QDockWidget(parent), + ui(new Ui::CommandQueueView) +{ + ui->setupUi(this); + connect(Project::get(),SIGNAL(commandListChanged()),SLOT(onCommandListChanged())); +} + +CommandQueueView::~CommandQueueView() +{ + delete ui; +} + +void CommandQueueView::changeEvent(QEvent *e) +{ + QDockWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void CommandQueueView::onCommandListChanged() { + Project &project(*Project::get()); + ui->lstQueuedCommands->clear(); + const CommandStream& cs(project.m_project_command_stream); + for(const Command * cmd : cs.m_commands) { + ui->lstQueuedCommands->addItem(cmd->name()); + } +} diff --git a/src/ui/CommandQueueView.h b/src/ui/CommandQueueView.h new file mode 100644 index 0000000..eb7cda8 --- /dev/null +++ b/src/ui/CommandQueueView.h @@ -0,0 +1,28 @@ +#ifndef COMMANDQUEUEVIEW_H +#define COMMANDQUEUEVIEW_H + +#include + +namespace Ui { +class CommandQueueView; +} + +class CommandQueueView : public QDockWidget +{ + Q_OBJECT + +public: + explicit CommandQueueView(QWidget *parent = 0); + ~CommandQueueView(); + +public slots: + void onCommandListChanged(); + +protected: + void changeEvent(QEvent *e); + +private: + Ui::CommandQueueView *ui; +}; + +#endif // COMMANDQUEUEVIEW_H diff --git a/src/ui/CommandQueueView.ui b/src/ui/CommandQueueView.ui new file mode 100644 index 0000000..0a759c8 --- /dev/null +++ b/src/ui/CommandQueueView.ui @@ -0,0 +1,33 @@ + + + CommandQueueView + + + + 0 + 0 + 222 + 446 + + + + Doc&kWidget + + + + + + + + + + Step + + + + + + + + + diff --git a/src/ui/DccMainWindow.cpp b/src/ui/DccMainWindow.cpp new file mode 100644 index 0000000..4900a6c --- /dev/null +++ b/src/ui/DccMainWindow.cpp @@ -0,0 +1,115 @@ +#include "DccMainWindow.h" +#include "ui_DccMainWindow.h" +//#include "ui_exe2c_gui.h" +//#include "exe2c_interface.h" +//#include "exe2c.h" +#include "FunctionViewWidget.h" +#include "FunctionListDockWidget.h" +#include "CommandQueueView.h" +#include "dcc_interface.h" +#include "project.h" + +#include +#include +#include +IDcc* g_EXE2C = NULL; +extern bool exe2c_Init(); + +DccMainWindow::DccMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::DccMainWindow) +{ + ui->setupUi(this); + ui->statusbar->addPermanentWidget(new QLabel("Test")); + + g_EXE2C = IDcc::get(); + g_EXE2C->BaseInit(); + g_EXE2C->Init(this); + + m_last_display = g_EXE2C->GetFirstFuncHandle(); + m_command_queue = new CommandQueueView(this); + m_functionlist_widget = new FunctionListDockWidget(this); + m_functionlist_widget->setWindowTitle(QApplication::tr("Function list")); + connect(m_functionlist_widget,SIGNAL(displayRequested()), SLOT(displayCurrentFunction())); + // we are beeing signalled when display is requested + connect(this,SIGNAL(functionListChanged()), m_functionlist_widget->model(),SLOT(updateFunctionList())); + this->addDockWidget(Qt::RightDockWidgetArea,m_functionlist_widget); + this->addDockWidget(Qt::LeftDockWidgetArea,m_command_queue); + m_asm_view = new FunctionViewWidget(this); + m_asm_view->setWindowTitle(tr("Assembly listing")); + ui->mdiArea->addSubWindow(m_asm_view); + //m_internal_view = new FunctionViewWidget; + //m_internal_view->setWindowTitle(QApplication::tr("Internal listing")); + //ui->mdiArea->addSubWindow(m_internal_view); + m_c_view = new FunctionViewWidget; + m_c_view->setWindowTitle(tr("Decompiled")); + ui->mdiArea->addSubWindow(m_c_view); +} + +DccMainWindow::~DccMainWindow() +{ + delete ui; +} + +void DccMainWindow::changeEvent(QEvent *e) +{ + QMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} +void DccMainWindow::onOptim() +{ + Project::get()->processCommands(); + g_EXE2C->analysis_Once(); + emit functionListChanged(); + if(m_last_display==g_EXE2C->GetCurFuncHandle()) + { + displayCurrentFunction(); + } +} +void DccMainWindow::onOptim10() +{ + for(int i=0; i<10; i++) + g_EXE2C->analysis_Once(); + emit functionListChanged(); + if(m_last_display==g_EXE2C->GetCurFuncHandle()) + { + displayCurrentFunction(); + } +} +void DccMainWindow::onOpenFile_Action() +{ + QFileDialog dlg; + QString name=dlg.getOpenFileName(0, + tr("Select DOS executable"), + ".", + tr("Executable files (*.exe *.EXE *.com *.COM)")); + if(!g_EXE2C->load(name)) { + QMessageBox::critical(this,tr("Error"),QString(tr("Cannot open file %1")).arg(name)); + } + //bool m_bSucc = m_xTextBuffer.LoadFromFile(lpszPathName); + emit functionListChanged(); +} + +void DccMainWindow::displayCurrentFunction() +{ + if(m_last_display!=g_EXE2C->GetCurFuncHandle()) + m_last_display=g_EXE2C->GetCurFuncHandle(); + g_EXE2C->prtout_asm(m_asm_view); + //g_EXE2C->prtout_itn(m_internal_view); + g_EXE2C->prtout_cpp(m_c_view); +} +void DccMainWindow::prt_log(const char *v) +{ + qDebug()<exit(0); +} diff --git a/src/ui/DccMainWindow.h b/src/ui/DccMainWindow.h new file mode 100644 index 0000000..adbf75c --- /dev/null +++ b/src/ui/DccMainWindow.h @@ -0,0 +1,49 @@ +#ifndef EXE2C_MAINWINDOW_H +#define EXE2C_MAINWINDOW_H + +#include "Procedure.h" +#include "DccFrontend.h" + +#include +#include +#include +#include + +class FunctionViewWidget; +class FunctionListDockWidget; +class CommandQueueView; + +namespace Ui { +class DccMainWindow; +} + +class DccMainWindow : public QMainWindow/*,public I_E2COUT*/ { + Q_OBJECT +public: + explicit DccMainWindow(QWidget *parent = 0); + ~DccMainWindow(); + void prt_log(const char * str); +public slots: + void onOptim(); + void onOptim10(); + void onOpenFile_Action(); + void displayCurrentFunction(); +signals: + void functionListChanged(); +protected: + void changeEvent(QEvent *e); +private slots: + void on_actionExit_triggered(); + +private: + + FunctionViewWidget *m_asm_view; + // FunctionViewWidget *m_internal_view; + FunctionViewWidget *m_c_view; + CommandQueueView *m_command_queue; + FunctionListDockWidget *m_functionlist_widget; + Ui::DccMainWindow *ui; + ilFunction m_last_display; +}; + +#endif // EXE2C_MAINWINDOW_H diff --git a/src/ui/DccMainWindow.ui b/src/ui/DccMainWindow.ui new file mode 100644 index 0000000..52357bc --- /dev/null +++ b/src/ui/DccMainWindow.ui @@ -0,0 +1,372 @@ + + + DccMainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + false + + + + + + + + + + + + + + + + + + + 0 + 0 + 800 + 18 + + + + + File + + + + + + + + + + + + Edit + + + + + + + + + + + + + + + + + + + + + View + + + + + + + Window + + + + + + Help + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + New + + + + + Open + + + + + Save + + + + + Save as + + + + + Exit + + + + + Undo + + + + + Redo + + + + + Cut + + + + + Copy + + + + + Paste + + + + + Delete + + + + + Select All + + + + + Find + + + + + Find Next + + + + + Find Previous + + + + + Replace + + + + + Read Only + + + + + Optim + + + Optimization step + + + + + true + + + Toolbar + + + Toggle toolbar visibility + + + Alt+T + + + + + true + + + Status Bar + + + Toggle status bar visibility + + + + + Cascade windows + + + Will re-arrange the mdi children windows + + + + + Tile windows + + + + + Optim10 + + + + + + + actionStatus_Bar + toggled(bool) + statusbar + setVisible(bool) + + + -1 + -1 + + + 399 + 588 + + + + + actionToolbar + toggled(bool) + toolBar + setVisible(bool) + + + -1 + -1 + + + 399 + 37 + + + + + actionMdiCascadeWindows + triggered() + mdiArea + cascadeSubWindows() + + + -1 + -1 + + + 347 + 314 + + + + + actionMdiTileWindows + triggered() + mdiArea + tileSubWindows() + + + -1 + -1 + + + 347 + 314 + + + + + actionOptim + triggered() + DccMainWindow + onOptim() + + + -1 + -1 + + + 399 + 299 + + + + + actionNew + triggered() + DccMainWindow + onOpenFile_Action() + + + -1 + -1 + + + 399 + 299 + + + + + actionOptim10 + triggered() + DccMainWindow + onOptim10() + + + -1 + -1 + + + 399 + 299 + + + + + + onOptim() + onOpenFile_Action() + displayCurrentFunction(QModelIndex) + functionSelected(QModelIndex) + onOptim10() + + diff --git a/src/ui/FunctionListDockWidget.cpp b/src/ui/FunctionListDockWidget.cpp new file mode 100644 index 0000000..eab9b69 --- /dev/null +++ b/src/ui/FunctionListDockWidget.cpp @@ -0,0 +1,102 @@ +#include "FunctionListDockWidget.h" +#include "ui_FunctionListDockWidget.h" +#include "dcc.h" +#include "dcc_interface.h" +#include "Procedure.h" + +#include +//#include "exe2c.h" +extern IDcc *g_EXE2C; +FunctionListDockWidget::FunctionListDockWidget(QWidget *parent) : + QDockWidget(parent), + ui(new Ui::FunctionListDockWidget) +{ + ui->setupUi(this); + ui->m_func_list_view->setModel(&m_list_model); +} + +FunctionListDockWidget::~FunctionListDockWidget() +{ + delete ui; +} +void FunctionListDockWidget::functionSelected(const QModelIndex &idx) +{ + QVariant v=m_list_model.data(idx,Qt::DisplayRole); + qDebug()<<"neb changed function to "<SetCurFunc_by_Name(v.toString().toStdString().c_str()); +} +// signalled by m_func_list_view accepted signal +void FunctionListDockWidget::displayRequest(const QModelIndex &) +{ + // argument ignored since functionSelected must've been called before us + emit displayRequested(); +} +void FunctionListModel::updateFunctionList() +{ + rebuildFunctionList(); +} +void FunctionListModel::rebuildFunctionList() +{ + ilFunction iter = g_EXE2C->GetFirstFuncHandle(); + clear(); + beginInsertRows(QModelIndex(),0,g_EXE2C->getFuncCount()); + + while (g_EXE2C->isValidFuncHandle(iter)) + { + const Function &info(*iter); + //g_EXE2C->GetFuncInfo(iter, &info); + iter = g_EXE2C->GetNextFuncHandle(iter); + + if (info.name.isEmpty()) + continue; + // fixme + add_function(info.name,info.nStep,info.procEntry,info.procEntry+10,info.cbParam); + } + endInsertRows(); +} +QVariant FunctionListModel::data(const QModelIndex &idx,int role) const +{ + int row=idx.row(); + int column=idx.column(); + const function_info &inf=m_list[row]; + if(Qt::DisplayRole==role) + { + switch(column) + { + case 0: // name + { + return QVariant(inf.m_name); + } + case 1: // step + return QVariant(inf.m_decoding_step); + case 2: // start offset + { + QString in_base_16=QString("%1").arg(inf.m_start_off,0,16); + return QVariant(in_base_16); + } + default: + return QVariant(); + + } + } + return QVariant(); +} +QVariant FunctionListModel::headerData(int section, Qt::Orientation orientation,int role) const +{ + if(Qt::DisplayRole==role && orientation==Qt::Horizontal) + { + switch(section) + { + case 0: // name + return QObject::tr("Function name"); + case 1: // step + return QObject::tr("Decoding step"); + case 2: // start offset + return QObject::tr("Start offset"); + default: + return QVariant(); + + } + } + return QVariant(); +} diff --git a/src/ui/FunctionListDockWidget.h b/src/ui/FunctionListDockWidget.h new file mode 100644 index 0000000..89e0935 --- /dev/null +++ b/src/ui/FunctionListDockWidget.h @@ -0,0 +1,72 @@ +#ifndef FUNCTIONLISTDOCKWIDGET_H +#define FUNCTIONLISTDOCKWIDGET_H + +#include +#include +//#include "exe2c.h" + +class FunctionListModel : public QAbstractTableModel +{ + Q_OBJECT + + struct function_info + { + QString m_name; + int m_decoding_step; + int m_start_off, m_end_off, m_stack_purge; + }; + std::vector m_list; +public: + int rowCount(const QModelIndex &/*idx*/) const {return m_list.size();} + int columnCount(const QModelIndex &/*idx*/) const {return 3;} + QVariant data(const QModelIndex &,int role) const; + void clear() + { + beginResetModel(); + m_list.clear(); + endResetModel(); + } + QVariant headerData(int section, Qt::Orientation orientation,int role) const; +public slots: + void updateFunctionList(); + +protected: + void add_function(const QString &name,int step,int start_off,int end_off,int stack_purge) + { + + function_info info; + info.m_name=name; + info.m_decoding_step=step; + info.m_start_off=start_off; + info.m_end_off=end_off; + info.m_stack_purge=stack_purge; + m_list.push_back(info); + } + void rebuildFunctionList(); + +}; + +namespace Ui { +class FunctionListDockWidget; +} + +class FunctionListDockWidget : public QDockWidget +{ + Q_OBJECT + +public: + explicit FunctionListDockWidget(QWidget *parent = 0); + ~FunctionListDockWidget(); + FunctionListModel *model() {return &m_list_model;} +public slots: + void displayRequest(const QModelIndex &idx); + void functionSelected(const QModelIndex &idx); + +signals: + void displayRequested(); +private: + Ui::FunctionListDockWidget *ui; + FunctionListModel m_list_model; +}; + +#endif // FUNCTIONLISTDOCKWIDGET_H diff --git a/src/ui/FunctionListDockWidget.ui b/src/ui/FunctionListDockWidget.ui new file mode 100644 index 0000000..702f93b --- /dev/null +++ b/src/ui/FunctionListDockWidget.ui @@ -0,0 +1,74 @@ + + + FunctionListDockWidget + + + + 0 + 0 + 400 + 300 + + + + DockWidget + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + m_func_list_view + activated(QModelIndex) + FunctionListDockWidget + displayRequest(QModelIndex) + + + 199 + 161 + + + 199 + 149 + + + + + m_func_list_view + clicked(QModelIndex) + FunctionListDockWidget + functionSelected(QModelIndex) + + + 199 + 161 + + + 199 + 149 + + + + + + displayRequested() + displayRequest(QModelIndex) + functionSelected(QModelIndex) + + diff --git a/src/ui/FunctionViewWidget.cpp b/src/ui/FunctionViewWidget.cpp new file mode 100644 index 0000000..145b6e9 --- /dev/null +++ b/src/ui/FunctionViewWidget.cpp @@ -0,0 +1,81 @@ +#include +#include +#include "FunctionViewWidget.h" +#include "ui_FunctionViewWidget.h" +#include "RenderTags.h" +//#include "XMLTYPE.h" +FunctionViewWidget::FunctionViewWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::FunctionViewWidget) +{ + ui->setupUi(this); + //ui->label->setTextFormat(Qt::RichText); +} + +FunctionViewWidget::~FunctionViewWidget() +{ + delete ui; +} + +void FunctionViewWidget::prtt(const char *s) +{ + collected_text+=s; + //collected_text+="
"; +} +void FunctionViewWidget::prtt(const std::string &s) +{ + collected_text+=s.c_str(); + //collected_text+="
"; +} +void FunctionViewWidget::TAGbegin(TAG_TYPE tag_type, void *p) +{ + QColor col= RenderTag_2_Color(tag_type); + switch(tag_type) + { + case XT_Function: + collected_text+=""; + break; + case XT_FuncName: + case XT_Symbol: + case XT_Keyword: + case XT_DataType: + case XT_Number: + case XT_AsmOffset: + case XT_AsmLabel: + collected_text+=""; + break; + default: + qDebug()<<"Tag type:"<"); + ui->textEdit->setHtml(collected_text); + collected_text.clear(); + break; + } + case XT_FuncName: + case XT_Symbol: + case XT_Keyword: + case XT_DataType: + case XT_Number: + case XT_AsmOffset: + case XT_AsmLabel: + collected_text+=""; + break; + default: + qDebug()<<"Tag end:"< +#include "StructuredTextTarget.h" +#include "RenderTags.h" +//#include "XmlPrt.h" +namespace Ui { +class FunctionViewWidget; +} +class FunctionViewWidget : public QWidget,public IStructuredTextTarget +{ + Q_OBJECT + +public: + explicit FunctionViewWidget(QWidget *parent = 0); + ~FunctionViewWidget(); + void prtt(const char * s); + void prtt(const std::string &s); + void TAGbegin(enum TAG_TYPE tag_type, void * p); + void TAGend(enum TAG_TYPE tag_type); +private: + Ui::FunctionViewWidget *ui; + QString collected_text; +}; + +#endif // FUNCTIONVIEWWIDGET_H diff --git a/src/ui/FunctionViewWidget.ui b/src/ui/FunctionViewWidget.ui new file mode 100644 index 0000000..8b44818 --- /dev/null +++ b/src/ui/FunctionViewWidget.ui @@ -0,0 +1,28 @@ + + + FunctionViewWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + true + + + + + + + + diff --git a/src/ui/RenderTags.cpp b/src/ui/RenderTags.cpp new file mode 100644 index 0000000..9b34402 --- /dev/null +++ b/src/ui/RenderTags.cpp @@ -0,0 +1,23 @@ +#include "RenderTags.h" + +QColor RenderTag_2_Color(TAG_TYPE tag_type) +{ + switch (tag_type) + { + case XT_invalid : return QColor(255,255,255); + case XT_blank : return QColor(255,255,255); + case XT_Symbol : return QColor(57,109,165); + case XT_Function : return QColor(255,255,255); + case XT_Keyword : return QColor(255,255,0); + case XT_Class : return QColor(255,255,0); + case XT_K1 : return QColor(163,70,255); + case XT_Comment : return QColor(0,245,255); + case XT_DataType : return QColor(100,222,192); + case XT_Number : return QColor(0,255,0); + case XT_AsmStack : return QColor(0,70,255); + case XT_AsmOffset: return QColor(70,180,70); + case XT_AsmLabel : return QColor(255,180,70); + case XT_FuncName : return QColor(255,0,255); + } + return QColor(255,255,255); +} diff --git a/src/ui/RenderTags.h b/src/ui/RenderTags.h new file mode 100644 index 0000000..c56afc4 --- /dev/null +++ b/src/ui/RenderTags.h @@ -0,0 +1,28 @@ +#pragma once + +#include +enum TAG_TYPE +{ + XT_invalid = 0, + XT_blank, + XT_Symbol, + XT_Function, + XT_Keyword, //Keywords, such as struct, union, for, while + XT_Class, //For comound types (struct/class/union) + XT_K1, //Braces {} [] + XT_Comment, //Comments + XT_DataType, // + XT_Number, // + XT_AsmStack, //stack values + XT_AsmOffset, //seg:offset + XT_AsmLabel, //label name + XT_FuncName, +}; +struct tColorPair +{ + QColor color1; + QColor color2; +}; +extern tColorPair tbl_color[]; + +QColor RenderTag_2_Color(TAG_TYPE tag_type);