From 3759e12a288531b34d931ec134ef0d9f2e4ab0ec Mon Sep 17 00:00:00 2001 From: Lesueur Benjamin Date: Sun, 31 Mar 2024 16:18:09 +0200 Subject: [PATCH] make IGCLBackend use delegates rather than DLLImport Still unable to fix CTL_RESULT_ERROR_OS_CALL and fix https://github.com/intel/drivers.gpu.control-library/issues/78 --- .../GraphicsProcessingUnit/IntelGPU.cs | 2 +- HandheldCompanion/IGCL/IGCLBackend.cs | 157 ++++++++++++------ HandheldCompanion/IGCL_Wrapper.dll | Bin 40960 -> 40960 bytes 3 files changed, 109 insertions(+), 50 deletions(-) diff --git a/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs b/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs index 3cdd91fc8..fcd70c9ee 100644 --- a/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs +++ b/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs @@ -149,7 +149,7 @@ private void TelemetryTimer_Elapsed(object? sender, ElapsedEventArgs e) { if (telemetryLock.TryEnter()) { - TelemetryData = GetTelemetryData(deviceIdx); + TelemetryData = GetTelemetry(deviceIdx); telemetryLock.Exit(); } } diff --git a/HandheldCompanion/IGCL/IGCLBackend.cs b/HandheldCompanion/IGCL/IGCLBackend.cs index 526c3ffd7..5b91bec82 100644 --- a/HandheldCompanion/IGCL/IGCLBackend.cs +++ b/HandheldCompanion/IGCL/IGCLBackend.cs @@ -384,70 +384,124 @@ public struct ctl_telemetry_data public double FanSpeedValue; } - [DllImport("IGCL_Wrapper.dll")] - private static extern ctl_result_t IntializeIgcl(); + [DllImport("kernel32")] + public static extern IntPtr LoadLibrary(string lpFileName); - [DllImport("IGCL_Wrapper.dll")] - private static extern void CloseIgcl(); - - [DllImport("IGCL_Wrapper.dll")] - public static extern uint GetAdapterCounts(); - - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr EnumerateDevices(ref uint pAdapterCount); - - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern ctl_result_t GetDeviceProperties(ctl_device_adapter_handle_t hDevice, ref ctl_device_adapter_properties_t StDeviceAdapterProperties); - - // RetroScaling - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetRetroScalingCaps(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_caps_t RetroScalingCaps); - - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetRetroScalingSettings(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_settings_t RetroScalingSettings); - - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t SetRetroScalingSettings(ctl_device_adapter_handle_t hDevice, ctl_retro_scaling_settings_t retroScalingSettings); - - // Scaling - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetScalingCaps(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_caps_t ScalingCaps); - - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetScalingSettings(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_settings_t ScalingSetting); - - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t SetScalingSettings(ctl_device_adapter_handle_t hDevice, uint idx, ctl_scaling_settings_t scalingSettings); - - // Sharpness - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetSharpnessCaps(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_caps_t SharpnessCaps); + [DllImport("kernel32", SetLastError = true)] + private static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)] + private static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName); + + // Define the function delegates with the same signatures as the functions in the DLL + private delegate ctl_result_t InitializeIgclDelegate(); + private delegate void CloseIgclDelegate(); + private delegate IntPtr EnumerateDevicesDelegate(ref uint pAdapterCount); + private delegate ctl_result_t GetDevicePropertiesDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_device_adapter_properties_t StDeviceAdapterProperties); + private delegate ctl_result_t GetRetroScalingCapsDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_caps_t RetroScalingCaps); + private delegate ctl_result_t GetRetroScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_settings_t RetroScalingSettings); + private delegate ctl_result_t SetRetroScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, ctl_retro_scaling_settings_t retroScalingSettings); + private delegate ctl_result_t GetScalingCapsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_caps_t ScalingCaps); + private delegate ctl_result_t GetScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_settings_t ScalingSetting); + private delegate ctl_result_t SetScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ctl_scaling_settings_t scalingSettings); + private delegate ctl_result_t GetSharpnessCapsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_caps_t SharpnessCaps); + private delegate ctl_result_t GetSharpnessSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_settings_t GetSharpness); + private delegate ctl_result_t SetSharpnessSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ctl_sharpness_settings_t SetSharpness); + private delegate ctl_result_t GetTelemetryDataDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_telemetry_data TelemetryData); + + // Define the function pointers + private static InitializeIgclDelegate InitializeIgcl; + private static CloseIgclDelegate CloseIgcl; + private static EnumerateDevicesDelegate EnumerateDevices; + private static GetDevicePropertiesDelegate GetDeviceProperties; + private static GetRetroScalingCapsDelegate GetRetroScalingCaps; + private static GetRetroScalingSettingsDelegate GetRetroScalingSettings; + private static SetRetroScalingSettingsDelegate SetRetroScalingSettings; + private static GetScalingCapsDelegate GetScalingCaps; + private static GetScalingSettingsDelegate GetScalingSettings; + private static SetScalingSettingsDelegate SetScalingSettings; + private static GetSharpnessCapsDelegate GetSharpnessCaps; + private static GetSharpnessSettingsDelegate GetSharpnessSettings; + private static SetSharpnessSettingsDelegate SetSharpnessSettings; + private static GetTelemetryDataDelegate GetTelemetryData; - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetSharpnessSettings(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_settings_t GetSharpness); + public static IntPtr[] devices = new IntPtr[1] { IntPtr.Zero }; + private static IntPtr pDll = IntPtr.Zero; - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t SetSharpnessSettings(ctl_device_adapter_handle_t hDevice, uint idx, ctl_sharpness_settings_t SetSharpness); + // for this support library + public enum IGCLStatus + { + NO_ERROR = 0, + DLL_NOT_FOUND = 1, + DLL_INCORRECT_VERSION = 2, + DLL_INITIALIZE_ERROR = 3 + } - [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)] - static extern ctl_result_t GetTelemetryData(ctl_device_adapter_handle_t hDevice, ref ctl_telemetry_data TelemetryData); + private const string dllName = "IGCL_Wrapper.dll"; + private static IGCLStatus status = IGCLStatus.NO_ERROR; - [DllImport("kernel32", SetLastError = true)] - static extern IntPtr LocalFree(IntPtr mem); + private static Delegate GetDelegate(string procName, Type delegateType) + { + IntPtr ptr = GetProcAddress(pDll, procName); + if (ptr != IntPtr.Zero) + { + var d = Marshal.GetDelegateForFunctionPointer(ptr, delegateType); + return d; + } - public static IntPtr[] devices = new IntPtr[1] { IntPtr.Zero }; + var result = Marshal.GetHRForLastWin32Error(); + throw Marshal.GetExceptionForHR(result); + } public static bool Initialize() { + pDll = LoadLibrary(dllName); + if (pDll == IntPtr.Zero) + { + status = IGCLStatus.DLL_NOT_FOUND; + } + else + { + try + { + // Get the function pointers + InitializeIgcl = (InitializeIgclDelegate)GetDelegate("IntializeIgcl", typeof(InitializeIgclDelegate)); + CloseIgcl = (CloseIgclDelegate)GetDelegate("CloseIgcl", typeof(CloseIgclDelegate)); + EnumerateDevices = (EnumerateDevicesDelegate)GetDelegate("EnumerateDevices", typeof(EnumerateDevicesDelegate)); + GetDeviceProperties = (GetDevicePropertiesDelegate)GetDelegate("GetDeviceProperties", typeof(GetDevicePropertiesDelegate)); + GetRetroScalingCaps = (GetRetroScalingCapsDelegate)GetDelegate("GetRetroScalingCaps", typeof(GetRetroScalingCapsDelegate)); + GetRetroScalingSettings = (GetRetroScalingSettingsDelegate)GetDelegate("GetRetroScalingSettings", typeof(GetRetroScalingSettingsDelegate)); + SetRetroScalingSettings = (SetRetroScalingSettingsDelegate)GetDelegate("SetRetroScalingSettings", typeof(SetRetroScalingSettingsDelegate)); + GetScalingCaps = (GetScalingCapsDelegate)GetDelegate("GetScalingCaps", typeof(GetScalingCapsDelegate)); + GetScalingSettings = (GetScalingSettingsDelegate)GetDelegate("GetScalingSettings", typeof(GetScalingSettingsDelegate)); + SetScalingSettings = (SetScalingSettingsDelegate)GetDelegate("SetScalingSettings", typeof(SetScalingSettingsDelegate)); + GetSharpnessCaps = (GetSharpnessCapsDelegate)GetDelegate("GetSharpnessCaps", typeof(GetSharpnessCapsDelegate)); + GetSharpnessSettings = (GetSharpnessSettingsDelegate)GetDelegate("GetSharpnessSettings", typeof(GetSharpnessSettingsDelegate)); + SetSharpnessSettings = (SetSharpnessSettingsDelegate)GetDelegate("SetSharpnessSettings", typeof(SetSharpnessSettingsDelegate)); + GetTelemetryData = (GetTelemetryDataDelegate)GetDelegate("GetTelemetryData", typeof(GetTelemetryDataDelegate)); + } + catch + { + status = IGCLStatus.DLL_INITIALIZE_ERROR; + } + } + + if (status != IGCLStatus.NO_ERROR) + return false; + ctl_result_t Result = ctl_result_t.CTL_RESULT_SUCCESS; // Call Init and check the result - Result = IntializeIgcl(); + Result = InitializeIgcl(); return Result == ctl_result_t.CTL_RESULT_SUCCESS; } public static int GetDeviceIdx(string deviceName) { + // test + Terminate(); + Initialize(); + ctl_result_t Result = ctl_result_t.CTL_RESULT_SUCCESS; uint adapterCount = 0; @@ -483,7 +537,12 @@ public static int GetDeviceIdx(string deviceName) public static void Terminate() { - CloseIgcl(); + if (pDll != IntPtr.Zero) + { + CloseIgcl(); + FreeLibrary(pDll); + pDll = IntPtr.Zero; + } } internal static bool HasGPUScalingSupport(nint deviceIdx, uint displayIdx) @@ -829,7 +888,7 @@ internal static bool SetIntegerScaling(nint deviceIdx, bool enabled, byte type) return RetroScalingSettings.Enable == enabled; } - internal static ctl_telemetry_data GetTelemetryData(nint deviceIdx) + public static ctl_telemetry_data GetTelemetry(nint deviceIdx) { ctl_result_t Result = ctl_result_t.CTL_RESULT_SUCCESS; ctl_telemetry_data TelemetryData = new(); diff --git a/HandheldCompanion/IGCL_Wrapper.dll b/HandheldCompanion/IGCL_Wrapper.dll index c49d5fc214f0402b427688a638eb461fd84dd69f..d6e22840760292ea630ca35275b506ea52a1634d 100644 GIT binary patch delta 12980 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 delta 12952 zcmeHNhkuk+*1m7ZBm-oal$kWrna~0u1u!5bfs_GK=^%kbiu7h8DBuJ_A0ojEii--d zu(}q&NKsINL6TLF#I@JNWo1}&2}MK}o$opK-pLGpf5P|W$1~^L^PF>Tec$)IGgQ=h zRn&P^5AnLib_^R~Yh3;4%59PU2`+4yU74^R?X6Fnd#@GJ)LXWLdT$r)fZkgn8)gTH z_O{4?gpbf}h5uth`uDC9@=Ah~fucQj-pskw2UCBAS&Yrv=o82Mb8gLc8QWM~OlPky z-B}?lW2N;3Pu6BcRCjc?To!iL>*%-8stI7()nxJ0__|R^Xj2>=Nb6)=kxHH zUc19Lt7WZUe9)+)v>y%5aUS^T9>vR3{SW3~TgwKrSs zhWEi`rPetPhcmXJt}H6kUT3W`<~`waQRZz?h>ehLT!mtvnqwquy?`o@(u2-7?LR zxR1WSj3E!F=sI!$HgF`>aF%^mV1+ADEgX;zH-U>xg$q*7c9HGI!<(V z4GA5uxkHoqg6JLr4PNP({9Ae~fYtt$)4ZIg>U(Jid9gm?{*k#9X#NKAjwk=?bXx6= zX#V`W)9Ey?AkJ=xkuI&|Qs#0QH`y`QrJLUs?pFA1aF$_24M_2o98rgS{BjpO}m$eckUiCOE})5qY;VOMoN$Fd{XB`{Q;6yF3=% z-?EA&_zj#R_m*pgpTkU@=02lniiS_6%OB*@3oiDFa)sX~yI6q-h5PTKecowaKZ<7r zM)N^_Cha`W_Zz8SC40`~Px&QuTq-uq5HOmb^9$1!^NW6wA^SvV`y8f7m-~5FL!$n& zSRlhTUSvqJ?UBrQjLc!|f=kVe^NKNc%MsJqNpo9q(>0BA7_HMhi8#A~NtdU{WjtIm zj7KKQt@IHNbX1-e0o`T5`D~H1f&#ixfPJD(&iq|s#LO3pfSaOi*ee5Qf{yFI$R12$ z^IvQaxK{+cF9R0KfCnhxJqoZ-^b`KY?f#vG|1Rl2EJX%PqJYO`fWHXn+aB=suQcb` zqK&=yi682+s7o;IWR>A4Cgxb~$M1BC^zBSbnD579I>&_c7vAm5|FhU|!%e=)n5ehO zMht()7`LEU?gh=)aj3I8LXDYXou+_uMBH$#{c^0B(|m_GyWvIYGM!w0yY4-TN7fbIsJR~4de}(2% zWvJ$P0r9pJxdt=wfN(^0!qKS}cR54KNHP2bT91v_T*Lnc6LFg75N9_SrArdI%!Et( z$q5!bjsI2QKT!IAKSHDoCI3|T(-l)dH>LALh=a36`1?x#gVMhv`Fq3vy3s#h9)#E1 z19C;cXQGV-D(2~dL$$s9(Ln3OAJ;N=&FG(LNxNnY(0w6V(@sG*oECb3?oG(FOiOxM z3jA^{g=c&Kdt180tN|P6~axVG_?aDK;`wc?|LJNK+mM z`Ol_DA`U-_y-Pi9-I8gs8Yk5?WLkP^OruBQ^EO^-&eq2956r`DV=`#(4QJD_mz#7r zW``N=xna*RvYWpF?YQ!@>zHM=(J?zuoZYZex)hPiS-2RFjJL##%llE`K9z)uX^FuTCu{kJ=_|%tA{=N%Zekiv zbA=4(B?G>qfHerf{E~!!XuJOz;a@n!CVKeoUNMLdC?HSuFhoQ+i4oW8E)fweduWsa zrzjvo_RwGWA8q%aE&Q*DwqdvQKScgN{acJEP51|FY7ZD70#5S$u<-B`a1@oAVLn#S z5${E#zy%*Mgs%%5q?Pg$VT1L<#5Nkf=YNESYeRTgcx1?8k*$5bdq|HYo)?~AJAq7> znwgkz+F&tZF`jLgT&w;AX5ut2B+hOyOP3=wAlas^i0W9JMb{ zJO{&JF~a3it;ggYIFWLx2BH%xeOFsnTJ5=(H2zT6NbM=Uv+F=@1plyWlvmfm{BqX_ zZ2;Ft#A(-fLPV6I61m=HSnS*?-F7}PVuzP`5Wf^LKd5yDvTpc$Sv)%Y6i!a_Nj@hs zx9d7(x4`}px^|jN29jr8Ss-5$*^ReFnn(3hK7-+N85`m>8JcEnIY2M3!jLHit^{_9(^TBBzF6^3z z{TJc?J=qVw& zcXI|Yh#r#r=E1w-%JH~%nvbT4xP^Cf8w8glxjW16imSpgaGF0)7IE9|=GI`zoaP54 z7x55_@CNg~68 zcr8V2i6mO*yCaHIi8$Fy&clN^+@FXUl8E&n-Vuber<}(;cdEl8)m}-IdJw;eh~bjZ zJcx1;@qG_DkGbwtFUomHBEy52Er@7Iv_9aDFeiz5beHq+AgaX7X9yxF)`Kt$!pXZQ zP6|6Zhp`MiZ0*K8Bixr%Q{+KHYtfb$kX?C>B4 zh)FJyM6w6bw>L!?CDA;~o$6nGh&a$qq*~@l;O>pHEr|dRVo?twuEdFmLo?l};^j0X zQRG4FN~DN>apE%h>3&4GFOwG&h--)ym&sb<)Pn^+IxcQF-b41XYUD4&%Y zkoeaUL{YJ~-V)-iH$ARN6Xe z5BVPN2%pQvxmAX%SXbAB<#+B#jmqdAgQzXK9+VLGf8XQX$u|^+VSMC@%CTJNrSLc02 zvH~8QHYPae?wFrM%zyYjgR)G=?vo*OxAqeuPlWTN_*By}MbH2iiGYG|F@PWt9+T`N zlZDX$Ooyhr2XL+zE%Np-5i|~jN6=2eCCZ?yQ`|wtf;b;4f;b3|AdBD@hw`*RnWi)q zMB`p9uAksgG489A-Pzv9p~O`kB6e+u2cZ+GmPjJmgLp^W2aJ+vp5#t7JC;%%2o|Z9 zc@Xh}D3C;e2l1`Es?8$e&_s8tX9Tfb5=9<_P7wVhadm<_;ul;an1@Nu!-F^{ZYfJ7 zk?cVXh@=RkB$~&&Qw@j`^9Yjj@E|6H5m6wC01u)noQNxdavtN{sTPS;+a*!tK@^M5 z`w1fG>R5NgfCx%;o=+T+9kg57X#;18E*|0sMnq~$`MD7TP01cn6GW6TKn$R{$epfO z5C{C_Ts(-^#J(3uBEW-KB#0|UK4}DwexXc7Bj_ViJz=CH6tqkcbo3{Lie=%SW%kut zxp&rH?P2~x*8VPAutLnZ;eC8I=pN>iv*S{|{E@H9aNUS%{B~#hlDoW@qe#ct`ns|h zr1}^H_ym3fllt)}`$tZ5BQLjh_X>;R&sf8?4g8pOnXMZ}YPEmp zGzUV#iWy~6Q|i5HoOh^zU77Hxn7v}Be@vh z%V=5gww#7XKq=;9ts=d$PLK9gIN+{7n#@~t$Hxdy8&+V8?j)!N-7c7@-v-@wXf&Ay z9etrm+A++WSr^pN3h$uM25*hJcH8ZSUZY{(lItDgGHQ70jCUCk_!3Tm!}$kZ3oB2j z)#De8aTDX|M~rl<{Y$6$!A=Ob*WNM-*K2Q#B6vb~yL9X+`Cs+IhwSqiPcHetSTqtL|07uTcr zo|U(?a(`>((3xG)b-9nzoQfRBXx8xQS$`HIFDgL886OAYEVVPxzI4L}#wuMF-#o_Z zpX_CAl!8>DUHQ}QD7AEMJ0iIarG;*SiEQrjTfWr-IQoI8V(3U-liyum410ZLd>%ia-%VT2ujlu{35+QS)Bei`7o=`Y@QgxBXow{J z|3ub-4Bza9CQA*;o zXg26tM#y8AxyA4;RO8zhO~%R{*bx{SVK|PZXImmq@q02U)@qS}tZ9~v7?yAFilz-0nKqPU-Tg2xd zUSja#^9S)cOV}rhPpkMGEI#{)&nORjC+KI-9@Og3rqg4R4ImYPq7!u>> z$9C&6T6nrhYt_~mFqbaWI-O*5wO`<2Bs^mjTRx7z*$rDO`rsOp#ZA`yMCOo1#kS)gaPf9IU$@C{=`?Qkl zl>B3tv>#Ig|4PZ*N``aE4_5MT%73AdHpY^a;pR^1xJ}7vDxkY+|ESu}Dk;73Ypfbz zqmu6``Gk@YYJx8Rmy}(!(Ld|HTT+Qix*U6|6}h48A1irG$^A<1R&tw?{(oA@rOG~9 z$uuRYpDg;A zt8Mp9VC-|?Ct%kk#$EvyOl55PeT-EDub_@T2;2r33VMczbZa_pZFt6SMspvkgMEJR zoz9pq+AWYjLyAB6?!(hKv26CrGRvaHWZMTm9a7nR7h95&5L6pAZ;lq(6ZfUWm9t9A zN|A6C6Tht|&X(q-ufl4^LMeP6SuLqh*<9HCap@^rf3mIQ-_Gw5P>s7t;{6Xk zNEZ2*Tmd&49VDV4KK)k%Yeg3`zGN6L-a|`-;ISTn73wqmVa%l*O)p;>pnV!X< z%P)b@m9W9?*$xZwX8Je0OF9&KB~cvyu~@wq)0YLYrZ~nP@5b1873bL&am(FtY3=

D-KGLP+nSFT^GWeqDhAizhb09u_8@Rt_+lOYmkpPZ%ZHy@*dtdA6E~!= zAdPuHC-)N1PZs8l_6WwZu{4DbGgdMIA6}U(O!k;W_PH?4U51Yp#C1vMLl#BY3VSlP z16U247}ftO7i!I< zXQo-ecwc7f@54V|6y>YN&TlNT#EzxCMZTV6asGjO>EPn;Hgw%MwE3XLT^ErTlCj~i zOb5m)ul~@fG+dzCtDtZ6;HkaIgYOvb!@~O;S@`nc7Sqz863R7)&1TxMZ9yf31w!jyS?Goc8=t7GKr1!y91H|-25Ww(#z*3+EDC~#-?E=IkAqmh1BqhTSXah=8zymR<;DHt(9hc@l zfDHf-)B!OA!4I^-0ZRHkZ~*AyD*@_&m_cv=T7dMy7!crpF9rfM0ZBv9DbNNK4h0YR z4?_&l0;CT|u5{!AVlrR{T7dKs=nG&Y!Q1fB1SDl58PEpM{{^ZB+JM4rBnJF*&@s>i z_*wxj|$0Bt~FA#~uS zeHbCo2pj{dfiD0Da0{pa4g$M@CZG=34731k0IPuy_Ibuy#@+=Y*D-cKPy<{BjFpUy z0>)Kh|7Ss232Xyi0BV6Vz~{hE!0$lk^^A1`1_EP%xj;E!2X+Gcf#bl(z>mN+pwkA% zqJdN(4=4pz13Q6NfaAdV4cPzhpj-z!Z$u}+2w)np4A=-f2OI^y0d4>tH(@`4dw>F9 zDX3{(Km1E+z10sjR|+i;TtZ1><}60icO z2A%_s0UrS016KgwDs&2@0(ro6U=6SxcoujCXaK$geg*s=W2_f26qo>%1M7e%fm)ym z_#XJ}F&56cY-g+oUr96dYg3U^K)!$gyT40+iZPCKK97c9O;NiFv zw%w&WV#EKxZ2rGpHtXn!%&L8A;{zS>7M1iyb&QuS{b8Kjc} zSdx{%YUu8&5%*UrCyRO@2?&yNfH$Gj6XH`~E|OAZVeQFSpGPntNLNkgS-VjU_Z{#w z21N2Ua1c7lPQ5TG=p+MyPoPtkvCM`Kv0RYnm5#DQR2^ie>SG5$I#nT8l}^=25kPjT zO3nbZ1yr33>?3umQuZsIs+Hir(oR*&My1=Re7UI_RL0B!C=r!2pDCTnn(ThkPUX!J zrBj&`fbxb0LFLXerBm5+0iZlo{`5`8qY^$;2CW6ipUR=uWb8jRs4kkAA_J&CIs}lN z>ZE{F)dA!(rBmJ1taPfMl2JR6Kh;q?luq^3Ri#s1RRoX^)mMjyAAtIb2r9Ui zDZK)+S?N@NCEueufZU;Us>iM>o$9h8Ky(0kNa<9k1q@R8A(ttg>b7RmZ7XoNpd=4g z9YDSWFdQ^0=rj~^l#~j)Ql(RYcS`A0=*6PIBOfaG)+(I}zgDGF0hlpV`cNTQqjV|= zZ5k>;(E;Q`0PPu7h|S7Qm16R6>Eo&xFTkfH9kT=|whl5NL+VsXE>k*Hl+8+~$})L` z$^*GW>NZhoUPHr#8B@tw1W?iv$TL6;>{NlqW=cC%qU!+ir;4;y*{L!ektOX^p;iHO z=%`X{1t=#~tRu3O4`fX?_MZqUVZCxBL8WXdKz1r=-&1xfZ4FkLluF!EK=?qOQaY8q zv7@A&O5e3grxLhT=~N2S|3aqNLK~DC)u7r~8zTv-kxP|Mwel&YQ_UQkt2%&Ot8}WN zTa`|=bVi=)0J28uR9kELDnDc?U=uTdQV-B_QPCY+pgMqDt8}WtTa`|=ct)Y>0I~`Y zOAXno?DQ(FE0T9iJ>*QFKens_@>w7qI=&*Yu{Jd5wo1}x9Pa|a2BlJrVn o@dyS+3cb%$gCxC^lTMP}!o3kp@*I%6sy6vAO}MRAE