From 5c4cd7a5b05979c56ac5426aecd60d9b72318040 Mon Sep 17 00:00:00 2001 From: Razor12911 Date: Fri, 11 Nov 2022 11:12:43 +0200 Subject: [PATCH] update to 0.6.6 --- changes.txt | 20 + common/LibImport.pas | 132 +++ common/Threading.pas | 1 + common/Utils.pas | 349 +++++-- contrib/opencl/DelphiCL.pas | 1777 -------------------------------- contrib/opencl/OpenCL.pas | 1032 ------------------- dbgenerator/DbgMain.pas | 52 +- dbgenerator/DbgUtils.pas | 5 + imports/BrunsliDLL.pas | 41 +- imports/FLACDLL.pas | 83 +- imports/FLZMA2DLL.pas | 214 +++- imports/GrittibanzliDLL.pas | 52 + imports/JoJpegDLL.pas | 26 +- imports/LZ4DLL.pas | 249 ++++- imports/LZODLL.pas | 80 +- imports/OodleDLL.pas | 88 +- imports/PackJPGDLL.pas | 32 +- imports/PreflateDLL.pas | 20 +- imports/ReflateDLL.pas | 47 +- imports/XDeltaDLL.pas | 25 +- imports/ZLibDLL.pas | 94 +- imports/ZSTDDLL.pas | 70 +- precompressor/PrecompEXE.pas | 61 +- precompressor/PrecompLZ4.pas | 82 +- precompressor/PrecompLZO.pas | 166 ++- precompressor/PrecompMain.pas | 104 +- precompressor/PrecompMedia.pas | 2 +- precompressor/PrecompOodle.pas | 253 ++--- precompressor/PrecompUtils.pas | 52 +- precompressor/PrecompZLib.pas | 2 +- precompressor/PrecompZSTD.pas | 6 +- xtool.dpr | 32 +- xtool.dproj | 440 ++------ xtool.dres | Bin 320648 -> 95816 bytes xtoolResource.rc | 3 +- 35 files changed, 1673 insertions(+), 4019 deletions(-) create mode 100644 common/LibImport.pas delete mode 100644 contrib/opencl/DelphiCL.pas delete mode 100644 contrib/opencl/OpenCL.pas create mode 100644 imports/GrittibanzliDLL.pas diff --git a/changes.txt b/changes.txt index a340325..c1631d3 100644 --- a/changes.txt +++ b/changes.txt @@ -1,8 +1,28 @@ + ES_R41 (0.6.6) + - fixed issues with exporting precompression database + - fixed issues with deduplication feature consuming a lot of system memory + - fixed oodle codec from auto enabling selkie method + - fixed reflate related checksum issues due to false positives + + ES_R40 (0.6.5) + - updated oodle scanner + - remove xdelta support from oodle and lzo codecs (crc mismatch often generates large diff files) + + ES_R39 (0.6.4) + - fixed issues with lzo2a and lzo1c codecs + + ES_R38 (0.6.3) + - added universal lz4f scanner + - fixed issues with database feature + - fixed issues with executable plugin support + - updated lzo codecs + ES_R37 (0.6.2) - added feature to inject libraries to main executable ES_R36 (0.6.1) - added fast lzma2 compression for portable mode + - fixed issues with wav stream detection - fixed minor issue with stream deduplication feature ES_R35 (0.6.0) diff --git a/common/LibImport.pas b/common/LibImport.pas new file mode 100644 index 0000000..73684dd --- /dev/null +++ b/common/LibImport.pas @@ -0,0 +1,132 @@ +unit LibImport; + +interface + +uses + MemoryModule, + WinAPI.Windows, + System.SysUtils, System.Classes, System.Character; + +type + TLibImport = class + private + FIsMemoryLib: Boolean; + FDLLLoaded: Boolean; + FDLLStream: TResourceStream; + FDLLHandle: NativeUInt; + public + constructor Create(ALibrary: String); + destructor Destroy; override; + function GetProcAddr(AProcName: PAnsiChar): Pointer; + property Loaded: Boolean read FDLLLoaded; + end; + +procedure InjectLib(Source, Dest: String); + +implementation + +function ResourceExists(ResName: String): Boolean; +begin + Result := FindResourceEx(HInstance, RT_RCDATA, PWideChar(ResName), 0) <> 0; +end; + +function FileToResourceName(FileName: String): String; +var + I: Integer; +begin + Result := ChangeFileExt(ExtractFileName(FileName), '').ToUpper; + for I := 1 to Result.Length do + if not Result[I].IsLetterOrDigit then + Result[I] := '_'; +end; + +procedure UpdateFileResource(Source, Dest, ResName: string); +var + Stream: TFileStream; + hDestRes: THandle; + lpData: Pointer; + cbData: DWORD; +begin + Stream := TFileStream.Create(Source, fmOpenRead or fmShareDenyNone); + try + Stream.Seek(0, soFromBeginning); + cbData := Stream.Size; + if cbData > 0 then + begin + GetMem(lpData, cbData); + try + Stream.ReadBuffer(lpData^, cbData); + hDestRes := BeginUpdateResource(PChar(Dest), False); + if hDestRes <> 0 then + if UpdateResource(hDestRes, RT_RCDATA, PWideChar(ResName), 0, lpData, + cbData) then + begin + if not EndUpdateResource(hDestRes, False) then + RaiseLastOSError; + end + else + RaiseLastOSError + else + RaiseLastOSError; + finally + FreeMem(lpData); + end; + end; + finally + Stream.Free; + end; +end; + +constructor TLibImport.Create(ALibrary: String); +var + LResName: String; +begin + inherited Create; + FDLLLoaded := False; + LResName := FileToResourceName(ALibrary); + FIsMemoryLib := ResourceExists(LResName); + if FIsMemoryLib then + begin + FDLLStream := TResourceStream.Create(HInstance, LResName, RT_RCDATA); + FDLLHandle := NativeUInt(MemoryLoadLibary(FDLLStream.Memory)); + FDLLLoaded := Assigned(Pointer(FDLLHandle)); + end + else + begin + FDLLHandle := LoadLibrary(PWideChar(ALibrary)); + FDLLLoaded := FDLLHandle >= 32; + end; +end; + +destructor TLibImport.Destroy; +begin + if FIsMemoryLib then + begin + if FDLLLoaded then + MemoryFreeLibrary(Pointer(FDLLHandle)); + FDLLStream.Free; + end + else if FDLLLoaded then + FreeLibrary(FDLLHandle); + inherited Destroy; +end; + +function TLibImport.GetProcAddr(AProcName: PAnsiChar): Pointer; +begin + if not FDLLLoaded then + Result := nil + else if FIsMemoryLib then + Result := MemoryGetProcAddress(Pointer(FDLLHandle), AProcName) + else + Result := GetProcAddress(FDLLHandle, AProcName); +end; + +procedure InjectLib(Source, Dest: String); +var + LResName: String; +begin + LResName := FileToResourceName(Source); + UpdateFileResource(Source, Dest, LResName); +end; + +end. diff --git a/common/Threading.pas b/common/Threading.pas index 3bfe59e..ececf18 100644 --- a/common/Threading.pas +++ b/common/Threading.pas @@ -220,6 +220,7 @@ function WaitForAny(const Tasks: array of TTask): Integer; var I: Integer; begin + Result := -1; while True do begin for I := Low(Tasks) to High(Tasks) do diff --git a/common/Utils.pas b/common/Utils.pas index 09cfbdd..8169d5a 100644 --- a/common/Utils.pas +++ b/common/Utils.pas @@ -221,17 +221,20 @@ type FIncSize = 64 * 1024 * 1024; private FStream: TFileStream; + FLastPosition, FLastSize: NativeInt; FMapHandle: THandle; FMapName: String; FMapBuffer: Pointer; function CalcSize(ASize: NativeInt): NativeInt; procedure IncMemory(ANewSize: NativeInt = 0); + procedure Flush(AForceFlush: Boolean = False); public constructor Create(const AMapName: String; AFileName: string); overload; constructor Create(const AMapName: String; ASize: NativeInt); overload; destructor Destroy; override; procedure SetSize(const NewSize: Int64); override; function Write(const Buffer; Count: LongInt): LongInt; override; + function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; end; TDownloadStream = class(TStream) @@ -253,33 +256,41 @@ type TBufferedStream = class(TStream) private - FStream: TStream; FReadMode: Boolean; FMemory: PByte; FBufferSize: Integer; FBufPos, FBufSize: Integer; public + Instance: TStream; constructor Create(Stream: TStream; ReadMode: Boolean; BufferSize: Integer = 65536); destructor Destroy; override; function Read(var Buffer; Count: Integer): Integer; override; function Write(const Buffer; Count: Integer): Integer; override; + procedure Flush; end; - { TGPUMemoryStream = class(TStream) - private - FMemory: Pointer; - FSize, FPosition: NativeInt; - protected - procedure SetPointer(Ptr: Pointer; const Size: NativeInt); - public - function Read(var Buffer; Count: longint): longint; override; - function Read(Buffer: TBytes; Offset, Count: longint): longint; override; - function Seek(const Offset: int64; Origin: TSeekOrigin): int64; override; - procedure SaveToStream(Stream: TStream); virtual; - procedure SaveToFile(const FileName: string); - property Memory: Pointer read FMemory; - end; } + TProcessStream = class(TStream) + private + FInput, FOutput: TStream; + FTask: TTask; + FProcessInfo: TProcessInformation; + FStdinr, FStdinw: THandle; + FStdoutr, FStdoutw: THandle; + FExecutable, FCommandLine, FWorkDir: String; + procedure ExecReadTask; + procedure ExecWriteTask; + public + constructor Create(AExecutable, ACommandLine, AWorkDir: String; + AInput: TStream = nil; AOutput: TStream = nil); + destructor Destroy; override; + function Read(var Buffer; Count: LongInt): LongInt; override; + function Write(const Buffer; Count: LongInt): LongInt; override; + function Execute: Boolean; + procedure Wait; + function Done: Boolean; + function Running: Boolean; + end; TDataStore = class(TObject) public @@ -311,7 +322,7 @@ type FDone, FFirstRead, FLastRead: Boolean; public constructor Create(AInput: TStream; ADynamic: Boolean; - ASlots, ASize: NativeInt; ATempFile: String = ''); + ASlots, ASize: NativeInt; ATempFile: String = 'datastore.tmp'); destructor Destroy; override; procedure ChangeInput(AInput: TStream); function Read(Index: Integer; Position: NativeInt; var Buffer; @@ -490,8 +501,6 @@ function GetCmdStr(CommandLine: String; Index: Integer; KeepQuotes: Boolean = False): string; function GetCmdCount(CommandLine: String): Integer; -procedure UpdateFileResource(Source, Dest, ResName: string); - implementation function GetBits(Data: Int64; Index: TInt64_BitIndex; @@ -1367,6 +1376,8 @@ var begin inherited Create(False); FStream := nil; + FLastPosition := FPosition; + FLastSize := 0; FMapHandle := 0; FMapBuffer := nil; LExists := FileExists(AFileName); @@ -1399,6 +1410,8 @@ var begin inherited Create(False); FStream := nil; + FLastPosition := FPosition; + FLastSize := 0; FMapHandle := 0; FMapBuffer := nil; FMapName := AMapName; @@ -1416,6 +1429,7 @@ end; destructor TSharedMemoryStream.Destroy; begin + Flush(True); UnMapViewOfFile(FMapBuffer); CloseHandle(FMapHandle); if Assigned(FStream) then @@ -1455,6 +1469,27 @@ begin [FMapName, SysErrorMessage(GetLastError)]); FMapBuffer := MapViewOfFile(FMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0); Update(FMapBuffer, LSize); + FLastPosition := FPosition; +end; + +procedure TSharedMemoryStream.Flush(AForceFlush: Boolean); +var + LAddr: Pointer; + LSize: NativeInt; +begin + if AForceFlush or (FLastSize >= FIncSize) or + (InRange(FPosition, FLastPosition, FLastPosition + FLastSize) = False) then + begin + if (FLastSize > 0) and Assigned(FMapBuffer) then + begin + LAddr := PByte(FMapBuffer) + FLastPosition; + LSize := Min(FLastSize, FSize - FLastPosition); + if LSize > 0 then + FlushViewOfFile(LAddr, FLastSize); + end; + FLastPosition := FPosition; + FLastSize := 0; + end; end; procedure TSharedMemoryStream.SetSize(const NewSize: Int64); @@ -1469,6 +1504,15 @@ begin if FPosition + Count > FMaxSize then IncMemory(FPosition + Count); Result := inherited Write(Buffer, Count); + Flush; + Inc(FLastSize, Result); +end; + +function TSharedMemoryStream.Seek(const Offset: Int64; + Origin: TSeekOrigin): Int64; +begin + Result := inherited Seek(Offset, Origin); + FLastPosition := FPosition; end; constructor TDownloadStream.Create(Url: string); @@ -1540,26 +1584,22 @@ constructor TBufferedStream.Create(Stream: TStream; ReadMode: Boolean; BufferSize: Integer); begin inherited Create; - FStream := Stream; + Instance := Stream; FReadMode := ReadMode; GetMem(FMemory, BufferSize); FBufferSize := BufferSize; FBufPos := 0; if FReadMode then - FBufSize := FStream.Read(FMemory^, FBufferSize) + FBufSize := Instance.Read(FMemory^, FBufferSize) else FBufSize := 0; end; destructor TBufferedStream.Destroy; begin - if FReadMode = False then - begin - FStream.WriteBuffer(FMemory^, FBufSize); - FBufSize := 0; - end; + Flush; FreeMem(FMemory); - FStream.Free; + Instance.Free; inherited Destroy; end; @@ -1599,7 +1639,7 @@ begin if FBufPos = FBufSize then begin FBufPos := 0; - FBufSize := FStream.Read(FMemory^, FBufferSize); + FBufSize := Instance.Read(FMemory^, FBufferSize); end; end; Result := Count - FCount; @@ -1622,12 +1662,12 @@ begin FDest := FMemory + FBufSize; if (FBufSize = 0) and (FCount >= FBufferSize) then begin - FStream.WriteBuffer(FSrc^, FBufferSize); + Instance.WriteBuffer(FSrc^, FBufferSize); Dec(FCount, FBufferSize); end else if (FBufSize = FBufferSize) then begin - FStream.WriteBuffer(FMemory^, FBufSize); + Instance.WriteBuffer(FMemory^, FBufSize); FBufSize := 0; end else @@ -1655,6 +1695,190 @@ begin Result := Count - FCount; end; +procedure TBufferedStream.Flush; +begin + if FReadMode = False then + begin + Instance.WriteBuffer(FMemory^, FBufSize); + FBufSize := 0; + end; +end; + +constructor TProcessStream.Create(AExecutable, ACommandLine, AWorkDir: String; + AInput: TStream; AOutput: TStream); +begin + inherited Create; + FInput := AInput; + FOutput := AOutput; + FExecutable := AExecutable; + FCommandLine := ACommandLine; + FWorkDir := AWorkDir; + FTask := TTask.Create; +end; + +destructor TProcessStream.Destroy; +begin + CloseHandleEx(FStdinr); + CloseHandleEx(FStdinw); + CloseHandleEx(FStdoutr); + CloseHandleEx(FStdoutw); + CloseHandleEx(FProcessInfo.hProcess); + FTask.Free; + inherited Destroy; +end; + +function TProcessStream.Read(var Buffer; Count: LongInt): LongInt; +var + BytesRead: DWORD; +begin + Result := 0; + if Assigned(FOutput) then + raise EReadError.CreateRes(@SReadError); + if ReadFile(FStdoutr, Buffer, Count, BytesRead, nil) then + Result := BytesRead; +end; + +function TProcessStream.Write(const Buffer; Count: LongInt): LongInt; +var + BytesWritten: DWORD; + Res: Boolean; +begin + Result := 0; + if Assigned(FInput) then + raise EWriteError.CreateRes(@SWriteError); + if WriteFile(FStdinw, Buffer, Count, BytesWritten, nil) then + Result := BytesWritten; +end; + +procedure TProcessStream.ExecReadTask; +const + BufferSize = 65536; +var + Buffer: array [0 .. BufferSize - 1] of Byte; + BytesRead: DWORD; +begin + while ReadFile(FStdoutr, Buffer[0], Length(Buffer), BytesRead, nil) and + (BytesRead > 0) do + FOutput.WriteBuffer(Buffer[0], BytesRead); + CloseHandleEx(FStdoutr); +end; + +procedure TProcessStream.ExecWriteTask; +const + BufferSize = 65536; +var + Buffer: array [0 .. BufferSize - 1] of Byte; + BytesWritten: DWORD; +begin + BytesWritten := FInput.Read(Buffer[0], BufferSize); + while WriteFile(FStdinw, Buffer[0], BytesWritten, BytesWritten, nil) and + (BytesWritten > 0) do + BytesWritten := FInput.Read(Buffer[0], BufferSize); + CloseHandleEx(FStdinw); +end; + +function TProcessStream.Execute: Boolean; +const + PipeSecurityAttributes: TSecurityAttributes = + (nLength: sizeof(PipeSecurityAttributes); bInheritHandle: True); +var + StartupInfo: TStartupInfo; + dwExitCode: DWORD; + LWorkDir: PChar; + LStream: THandleStream; +begin + Result := False; + CreatePipe(FStdinr, FStdinw, @PipeSecurityAttributes, 0); + CreatePipe(FStdoutr, FStdoutw, @PipeSecurityAttributes, 0); + SetHandleInformation(FStdinw, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(FStdoutr, HANDLE_FLAG_INHERIT, 0); + ZeroMemory(@StartupInfo, sizeof(StartupInfo)); + StartupInfo.cb := sizeof(StartupInfo); + StartupInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow := SW_HIDE; + StartupInfo.hStdInput := FStdinr; + StartupInfo.hStdOutput := FStdoutw; + StartupInfo.hStdError := 0; + ZeroMemory(@FProcessInfo, sizeof(FProcessInfo)); + if FWorkDir <> '' then + LWorkDir := Pointer(FWorkDir) + else + LWorkDir := Pointer(GetCurrentDir); + if CreateProcess(nil, PChar('"' + FExecutable + '" ' + FCommandLine), nil, + nil, True, NORMAL_PRIORITY_CLASS, nil, LWorkDir, StartupInfo, FProcessInfo) + then + begin + CloseHandleEx(FProcessInfo.hThread); + CloseHandleEx(FStdinr); + CloseHandleEx(FStdoutw); + if Assigned(FOutput) and not Assigned(FInput) then + begin + FTask.Perform(ExecReadTask); + FTask.Start; + Result := True; + end + else if Assigned(FInput) and not Assigned(FOutput) then + begin + FTask.Perform(ExecWriteTask); + FTask.Start; + Result := True; + end + else if Assigned(FInput) and Assigned(FOutput) then + begin + FTask.Perform(ExecReadTask); + FTask.Start; + LStream := THandleStream.Create(FStdinw); + try + CopyStream(FInput, LStream); + finally + LStream.Free; + CloseHandleEx(FStdinw); + FTask.Wait; + end; + WaitForSingleObject(FProcessInfo.hProcess, INFINITE); + GetExitCodeProcess(FProcessInfo.hProcess, dwExitCode); + CloseHandleEx(FProcessInfo.hProcess); + if FTask.Status <> TThreadStatus.tsErrored then + FTask.RaiseLastError; + Result := dwExitCode = 0; + end; + end + else + begin + CloseHandleEx(FStdinr); + CloseHandleEx(FStdinw); + CloseHandleEx(FStdoutr); + CloseHandleEx(FStdoutw); + RaiseLastOSError; + end; +end; + +procedure TProcessStream.Wait; +begin + WaitForSingleObject(FProcessInfo.hProcess, INFINITE); +end; + +function TProcessStream.Done: Boolean; +var + dwExitCode: DWORD; +begin + Result := False; + CloseHandleEx(FStdinw); + CloseHandleEx(FStdoutr); + FTask.Wait; + WaitForSingleObject(FProcessInfo.hProcess, INFINITE); + GetExitCodeProcess(FProcessInfo.hProcess, dwExitCode); + CloseHandleEx(FProcessInfo.hProcess); + if FTask.Status <> TThreadStatus.tsErrored then + FTask.RaiseLastError; + Result := dwExitCode = 0; +end; + +function TProcessStream.Running: Boolean; +begin + Result := WaitForSingleObject(FProcessInfo.hProcess, 0) = WAIT_TIMEOUT; +end; + constructor TDataStore1.Create(AInput: TStream; ADynamic: Boolean; ASlots, ASize: NativeInt; ATempFile: String); var @@ -3069,6 +3293,8 @@ begin if AnsiContainsStr(S, 'p') or AnsiContainsStr(S, '%') then begin Result := Round((CPUCount * StrToInt(Copy(S, 1, Length(S) - 1))) / 100); + if Result < 1 then + Result := 1; exit; end; Result := StrToInt64(S); @@ -3344,10 +3570,10 @@ begin FileWriteBuffer(hstdinw, InBuff^, InSize); finally CloseHandleEx(hstdinw); - WaitForSingleObject(ProcessInfo.hProcess, INFINITE); - GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); - CloseHandleEx(ProcessInfo.hProcess); end; + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); + CloseHandleEx(ProcessInfo.hProcess); Result := dwExitCode = 0; end else @@ -3399,10 +3625,10 @@ begin Output(@Buffer[0], BytesRead); finally CloseHandleEx(hstdoutr); - WaitForSingleObject(ProcessInfo.hProcess, INFINITE); - GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); - CloseHandleEx(ProcessInfo.hProcess); end; + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); + CloseHandleEx(ProcessInfo.hProcess); Result := dwExitCode = 0; end else @@ -3429,7 +3655,7 @@ var dwExitCode: DWORD; LWorkDir: PChar; begin - Result := True; + Result := False; CreatePipe(hstdinr, hstdinw, @PipeSecurityAttributes, 0); CreatePipe(hstdoutr, hstdoutw, @PipeSecurityAttributes, 0); SetHandleInformation(hstdinw, HANDLE_FLAG_INHERIT, 0); @@ -3461,10 +3687,10 @@ begin finally CloseHandleEx(hstdinw); CloseHandleEx(hstdoutr); - WaitForSingleObject(ProcessInfo.hProcess, INFINITE); - GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); - CloseHandleEx(ProcessInfo.hProcess); end; + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); + CloseHandleEx(ProcessInfo.hProcess); Result := dwExitCode = 0; end else @@ -3506,7 +3732,7 @@ var LTask: TTask; LDone: Boolean; begin - Result := True; + Result := False; CreatePipe(hstdinr, hstdinw, @PipeSecurityAttributes, 0); CreatePipe(hstdoutr, hstdoutw, @PipeSecurityAttributes, 0); SetHandleInformation(hstdinw, HANDLE_FLAG_INHERIT, 0); @@ -3544,6 +3770,9 @@ begin end; CloseHandleEx(hstdoutr); end; + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + GetExitCodeProcess(ProcessInfo.hProcess, dwExitCode); + CloseHandleEx(ProcessInfo.hProcess); if Assigned(LTask) then if LTask.Status <> TThreadStatus.tsErrored then try @@ -3576,7 +3805,8 @@ begin while Idx <= Index do begin Quoted := False; - while (I <= CommandLine.Length) and (CommandLine[I] = ' ') do + while (I <= CommandLine.Length) and + ((CommandLine[I] = ' ') or (CommandLine[I] = #09)) do Inc(I); if I > CommandLine.Length then break; @@ -3617,41 +3847,4 @@ begin Inc(Result); end; -procedure UpdateFileResource(Source, Dest, ResName: string); -var - Stream: TFileStream; - hDestRes: THandle; - lpData: Pointer; - cbData: DWORD; -begin - Stream := TFileStream.Create(Source, fmOpenRead or fmShareDenyNone); - try - Stream.Seek(0, soFromBeginning); - cbData := Stream.Size; - if cbData > 0 then - begin - GetMem(lpData, cbData); - try - Stream.Read(lpData^, cbData); - hDestRes := BeginUpdateResource(PChar(Dest), False); - if hDestRes <> 0 then - if UpdateResource(hDestRes, RT_RCDATA, PWideChar(ResName), 0, lpData, - cbData) then - begin - if not EndUpdateResource(hDestRes, False) then - RaiseLastOSError - end - else - RaiseLastOSError - else - RaiseLastOSError; - finally - FreeMem(lpData); - end; - end; - finally - Stream.Free; - end; -end; - end. diff --git a/contrib/opencl/DelphiCL.pas b/contrib/opencl/DelphiCL.pas deleted file mode 100644 index 1552309..0000000 --- a/contrib/opencl/DelphiCL.pas +++ /dev/null @@ -1,1777 +0,0 @@ -(* ****************************************** *) -(* *) -(* DelphiCL *) -(* *) -(* created by : Maksym Tymkovych *) -(* (niello) *) -(* *) -(* headers versions: 0.07 *) -(* file name : DelphiCL.pas *) -(* last modify : 10.12.11 *) -(* license : BSD *) -(* *) -(* Site : www.niello.org.ua *) -(* e-mail : muxamed13@ukr.net *) -(* ICQ : 446-769-253 *) -(* *) -(* ********Copyright (c) niello 2008-2011**** *) - -unit DelphiCL; - -interface - -uses - OpenCL, - Windows, - SysUtils; - -type - - TDCLMemFlags = (mfReadWrite, mfWriteOnly, mfReadOnly, mfUseHostPtr, - mfAllocHostPtr, mfCopyHostPtr); - TDCLMemFlagsSet = set of TDCLMemFlags; - - TDCLBuffer = class - private - FMem: PCL_mem; - FStatus: CL_int; - FSize: Size_t; - protected - constructor Create(const Context: PCL_context; const Flags: TDCLMemFlagsSet; - const Size: Size_t; const Data: Pointer = nil); - public - procedure Free(); - property Size: Size_t read FSize; - property Status: CL_int read FStatus; - end; - - TDCLImage2D = class - private - FMem: PCL_mem; - FStatus: CL_int; - FFormat: CL_image_format; - FWidth: Size_t; - FHeight: Size_t; - FRowPitch: Size_t; - protected - constructor Create(const Context: PCL_context; const Flags: TDCLMemFlagsSet; - const Format: PCL_image_format; const Width, Height: Size_t; - const RowPitch: Size_t = 0; const Data: Pointer = nil); - public - procedure Free(); - property Width: Size_t read FWidth; - property Height: Size_t read FHeight; - property RowPitch: Size_t read FRowPitch; - property Status: CL_int read FStatus; - end; - - TDCLCommandQueueProperties = (cqpNone, cqpOutOfOrderExecModeEnable); - TDCLCommandQueuePropertiesSet = set of TDCLCommandQueueProperties; - - TDCLKernel = class - private - FKernel: PCL_kernel; - FStatus: CL_int; - protected - constructor Create(const Program_: PCL_program; const KernelName: PPChar); - function GetFunctionName(): AnsiString; - function GetNumArgs(): CL_uint; - public - property Status: CL_int read FStatus; - property FunctionName: AnsiString read GetFunctionName; - property NumArgs: CL_uint read GetNumArgs; - procedure SetArg(const Index: CL_uint; const Size: Size_t; - const Value: Pointer); overload; - procedure SetArg(const Index: CL_uint; const Value: TDCLBuffer); overload; - procedure SetArg(const Index: CL_uint; const Value: TDCLImage2D); overload; - procedure Free(); - end; - - TDCLCommandQueue = class - private - FCommandQueue: PCL_command_queue; - FStatus: CL_int; - FProperties: TDCLCommandQueuePropertiesSet; - constructor Create(const Device_Id: PCL_device_id; - const Context: PCL_context; - const Properties: TDCLCommandQueuePropertiesSet = [cqpNone]); - public - procedure ReadBuffer(const Buffer: TDCLBuffer; const Size: Size_t; - const Data: Pointer); - procedure WriteBuffer(const Buffer: TDCLBuffer; const Size: Size_t; - const Data: Pointer); - procedure ReadImage2D(const Image: TDCLImage2D; const Width, Height: Size_t; - const Data: Pointer); - procedure WriteImage2D(const Image: TDCLImage2D; - const Width, Height: Size_t; const Data: Pointer); - procedure Execute(const Kernel: TDCLKernel; const Size: Size_t); overload; - procedure Execute(const Kernel: TDCLKernel; // const Device: PCL_device_id; - const Size: array of Size_t); overload; - property Status: CL_int read FStatus; - property Properties: TDCLCommandQueuePropertiesSet read FProperties; - procedure Free(); - end; - - TArraySize_t = Array of Size_t; - - TDCLProgram = class - private - FProgram: PCL_program; - FStatus: CL_int; - FSource: PAnsiChar; - FBinarySizesCount: Size_t; - FBinarySizes: TArraySize_t; - // FBinaries: PByte; - protected - constructor Create(const Context: PCL_context; const Source: PPChar; - const Options: PPChar = nil); - function GetBinarySizes(const Index: Size_t): Size_t; - public - property BinarySizes[const Index: Size_t]: Size_t read GetBinarySizes; - property BinarySizesCount: Size_t read FBinarySizesCount; - property Source: PAnsiChar read FSource; - property Status: CL_int read FStatus; - function CreateKernel(const KernelName: PPChar): TDCLKernel; - procedure Free(); - end; - - TDCLContext = class - private - FContext: PCL_context; - FStatus: CL_int; - FNumDevices: CL_uint; - protected - // property Context: PCL_context read FContext; - public - constructor Create(Device_Id: PCL_device_id); - property Status: CL_int read FStatus; - property NumDevices: CL_uint read FNumDevices; - procedure Free(); - end; - - TDCLDevice = class - // private - FDevice_id: PCL_device_id; - private - FStatus: CL_int; - - FName: AnsiString; - FVendor: AnsiString; - FVersion: AnsiString; - FProfile: AnsiString; - - FIsCPU: Boolean; - FIsGPU: Boolean; - FIsAccelerator: Boolean; - FIsDefault: Boolean; - - FMaxWorkGroupSize: Size_t; - - FNativeVectorPreferredChar: CL_uint; - FNativeVectorPreferredShort: CL_uint; - FNativeVectorPreferredInt: CL_uint; - FNativeVectorPreferredLong: CL_uint; - FNativeVectorPreferredFloat: CL_uint; - FNativeVectorPreferredDouble: CL_uint; - FNativeVectorPreferredHalf: CL_uint; - FNativeVectorWidthChar: CL_uint; - FNativeVectorWidthShort: CL_uint; - FNativeVectorWidthInt: CL_uint; - FNativeVectorWidthLong: CL_uint; - FNativeVectorWidthFloat: CL_uint; - FNativeVectorWidthDouble: CL_uint; - FNativeVectorWidthHalf: CL_uint; - - FMaxClockFrequency: CL_uint; - FAddressBits: CL_uint; - FMaxMemAllocSize: CL_ulong; - - FIsImageSupport: Boolean; - - FMaxReadImageArgs: CL_uint; - FMaxWriteImageArgs: CL_uint; - FImage2DMaxWidth: Size_t; - FImage2DMaxHeight: Size_t; - FImage3DMaxWidth: Size_t; - FImage3DMaxHeight: Size_t; - FImage3DMaxDepth: Size_t; - FMaxSamplers: CL_uint; - FMaxParameterSize: Size_t; - FMemBaseAddrAlign: CL_uint; - FMinDataTypeAlignSize: CL_uint; - - FGlobalMemCacheLineSize: CL_uint; - FGlobalMemCacheSize: CL_ulong; - FGlobalMemSize: CL_ulong; - FMaxConstantBufferSize: CL_ulong; - FMaxConstantArgs: CL_uint; - - FLocalMemSize: CL_ulong; - FIsErrorCorrectionSupport: Boolean; - FIsHostUnifiedMemory: Boolean; - FProfilingTimerResolution: Size_t; - FIsEndianLittle: Boolean; - FIsAvailable: Boolean; - FIsCompilerAvailable: Boolean; - - FVendorId: CL_uint; - FMaxComputeUnits: CL_uint; - FMaxWorkItemDimensions: CL_uint; - FExtensionsString: AnsiString; - FOpenCLCVersion: AnsiString; - FDriverVersion: AnsiString; - - FExtensionsCount: Size_t; - FExtensions: Array of AnsiString; - - FContext: TDCLContext; - - function GetExtensions(const Index: Size_t): AnsiString; - function IsPresentExtension(const ExtensionName: AnsiString): Boolean; - protected - constructor Create(Device_Id: PCL_device_id); - property Device_Id: PCL_device_id read FDevice_id; - public - property Status: CL_int read FStatus; - - property Name: AnsiString read FName; - property Vendor: AnsiString read FVendor; - property Version: AnsiString read FVersion; - property Profile: AnsiString read FProfile; - - property IsCPU: Boolean read FIsCPU; - property IsGPU: Boolean read FIsGPU; - property IsAccelerator: Boolean read FIsAccelerator; - property IsDefault: Boolean read FIsDefault; - - property MaxWorkGroupSize: Size_t read FMaxWorkGroupSize; - - property NativeVectorPreferredChar: CL_uint read FNativeVectorPreferredChar; - property NativeVectorPreferredShort: CL_uint - read FNativeVectorPreferredShort; - property NativeVectorPreferredInt: CL_uint read FNativeVectorPreferredInt; - property NativeVectorPreferredLong: CL_uint read FNativeVectorPreferredLong; - property NativeVectorPreferredFloat: CL_uint - read FNativeVectorPreferredFloat; - property NativeVectorPreferredDouble: CL_uint - read FNativeVectorPreferredDouble; - property NativeVectorPreferredHalf: CL_uint read FNativeVectorPreferredHalf; - property NativeVectorWidthChar: CL_uint read FNativeVectorWidthChar; - property NativeVectorWidthShort: CL_uint read FNativeVectorWidthShort; - property NativeVectorWidthInt: CL_uint read FNativeVectorWidthInt; - property NativeVectorWidthLong: CL_uint read FNativeVectorWidthLong; - property NativeVectorWidthFloat: CL_uint read FNativeVectorWidthFloat; - property NativeVectorWidthDouble: CL_uint read FNativeVectorWidthDouble; - property NativeVectorWidthHalf: CL_uint read FNativeVectorWidthHalf; - - property MaxClockFrequency: CL_uint read FMaxClockFrequency; - property AddressBits: CL_uint read FAddressBits; - property MaxMemAllocSize: CL_ulong read FMaxMemAllocSize; - - property IsImageSupport: Boolean read FIsImageSupport; - - property MaxReadImageArgs: CL_uint read FMaxReadImageArgs; - property MaxWriteImageArgs: CL_uint read FMaxWriteImageArgs; - property Image2DMaxWidth: Size_t read FImage2DMaxWidth; - property Image2DMaxHeight: Size_t read FImage2DMaxHeight; - property Image3DMaxWidth: Size_t read FImage3DMaxWidth; - property Image3DMaxHeight: Size_t read FImage3DMaxHeight; - property Image3DMaxDepth: Size_t read FImage3DMaxDepth; - property MaxSamplers: CL_uint read FMaxSamplers; - property MaxParameterSize: Size_t read FMaxParameterSize; - property MemBaseAddrAlign: CL_uint read FMemBaseAddrAlign; - property MinDataTypeAlignSize: CL_uint read FMinDataTypeAlignSize; - - property GlobalMemCacheLineSize: CL_uint read FGlobalMemCacheLineSize; - property GlobalMemCacheSize: CL_ulong read FGlobalMemCacheSize; - property GlobalMemSize: CL_ulong read FGlobalMemSize; - property MaxConstantBufferSize: CL_ulong read FMaxConstantBufferSize; - property MaxConstantArgs: CL_uint read FMaxConstantArgs; - - property LocalMemSize: CL_ulong read FLocalMemSize; - property IsErrorCorrectionSupport: Boolean read FIsErrorCorrectionSupport; - property IsHostUnifiedMemory: Boolean read FIsHostUnifiedMemory; - property ProfilingTimerResolution: Size_t read FProfilingTimerResolution; - property IsEndianLittle: Boolean read FIsEndianLittle; - property IsAvailable: Boolean read FIsAvailable; - property IsCompilerAvailable: Boolean read FIsCompilerAvailable; - - property VendorId: CL_uint read FVendorId; - property MaxComputeUnits: CL_uint read FMaxComputeUnits; - property MaxWorkItemDimensions: CL_uint read FMaxWorkItemDimensions; - - property DriverVersion: AnsiString read FDriverVersion; - property OpenCLCVersion: AnsiString read FOpenCLCVersion; - property ExtensionsString: AnsiString read FExtensionsString; - - property Context: TDCLContext read FContext; - function CreateContext(): TDCLContext; - function CreateCommandQueue(const Properties - : TDCLCommandQueuePropertiesSet = [cqpNone]): TDCLCommandQueue; - function CreateBuffer(const Size: Size_t; const Data: Pointer = nil; - const Flags: TDCLMemFlagsSet = [mfReadWrite]): TDCLBuffer; - function CreateImage2D(const Format: PCL_image_format; - const Width, Height, RowPitch: Size_t; const Data: Pointer = nil; - const Flags: TDCLMemFlagsSet = [mfReadWrite]): TDCLImage2D; - function CreateProgram(const Source: PPChar; const Options: PPChar = nil) - : TDCLProgram; overload; - function CreateProgram(const FileName: String; const Options: PPChar = nil) - : TDCLProgram; overload; - - property ExtensionsCount: Size_t read FExtensionsCount; - property Extensions[const Index: Size_t]: AnsiString read GetExtensions; - property IsSupportedExtension[const Index: AnsiString]: Boolean - read IsPresentExtension; - procedure Free(); - end; - - TDCLPlatform = class - private - FPlatform_id: PCL_platform_id; - FProfile: AnsiString; - FVersion: AnsiString; - FName: AnsiString; - FVendor: AnsiString; - FExtensionsString: AnsiString; - FStatus: CL_int; - FDevices: Array of TDCLDevice; - FDeviceCount: CL_uint; - FExtensionsCount: Size_t; - FExtensions: Array of AnsiString; - function GetDevice(Index: CL_uint): TDCLDevice; - function GetExtensions(Index: Size_t): AnsiString; - function IsPresentExtension(const ExtensionName: AnsiString): Boolean; - - function GetDeviceWithMaxClockFrequency(): TDCLDevice; - function GetDeviceWithMaxComputeUnits(): TDCLDevice; - - function GetDeviceWithMaxGlobalMemCacheLineSize(): TDCLDevice; - function GetDeviceWithMaxGlobalMemCacheSize(): TDCLDevice; - function GetDeviceWithMaxGlobalMemSize(): TDCLDevice; - - function GetDeviceWithMaxImage2DWidth(): TDCLDevice; - function GetDeviceWithMaxImage2DHeight(): TDCLDevice; - function GetDeviceWithMaxImage3DWidth(): TDCLDevice; - function GetDeviceWithMaxImage3DHeight(): TDCLDevice; - function GetDeviceWithMaxImage3DDepth(): TDCLDevice; - - function GetDeviceWithMaxLocalMemSize(): TDCLDevice; - function GetDeviceWithMaxConstantArgs(): TDCLDevice; - function GetDeviceWithMaxConstantBufferSize(): TDCLDevice; - function GetDeviceWithMaxMemAllocSize(): TDCLDevice; - function GetDeviceWithMaxParameterSize(): TDCLDevice; - function GetDeviceWithMaxReadImageArgs(): TDCLDevice; - function GetDeviceWithMaxSamplers(): TDCLDevice; - function GetDeviceWithMaxWorkGroupSize(): TDCLDevice; - function GetDeviceWithMaxWorkItemDimensions(): TDCLDevice; - function GetDeviceWithMaxWriteImageArgs(): TDCLDevice; - public - constructor Create(Platform_id: PCL_platform_id); - property Profile: AnsiString read FProfile; - property Version: AnsiString read FVersion; - property Name: AnsiString read FName; - property Vendor: AnsiString read FVendor; - property ExtensionsString: AnsiString read FExtensionsString; - - property DeviceCount: CL_uint read FDeviceCount; - property Status: CL_int read FStatus; - property Devices[Index: CL_uint]: TDCLDevice read GetDevice; - property ExtensionsCount: Size_t read FExtensionsCount; - property Extensions[Index: Size_t]: AnsiString read GetExtensions; - property IsSupportedExtension[const Index: AnsiString]: Boolean - read IsPresentExtension; - - property DeviceWithMaxClockFrequency: TDCLDevice - read GetDeviceWithMaxClockFrequency; - property DeviceWithMaxComputeUnits: TDCLDevice - read GetDeviceWithMaxComputeUnits; - property DeviceWithMaxGlobalMemCacheLineSize: TDCLDevice - read GetDeviceWithMaxGlobalMemCacheLineSize; - property DeviceWithMaxGlobalMemCacheSize: TDCLDevice - read GetDeviceWithMaxGlobalMemCacheSize; - property DeviceWithMaxGlobalMemSize: TDCLDevice - read GetDeviceWithMaxGlobalMemSize; - property DeviceWithMaxImage2DWidth: TDCLDevice - read GetDeviceWithMaxImage2DWidth; - property DeviceWithMaxImage2DHeight: TDCLDevice - read GetDeviceWithMaxImage2DHeight; - property DeviceWithMaxImage3DWidth: TDCLDevice - read GetDeviceWithMaxImage3DWidth; - property DeviceWithMaxImage3DHeight: TDCLDevice - read GetDeviceWithMaxImage3DHeight; - property DeviceWithMaxImage3DDepth: TDCLDevice - read GetDeviceWithMaxImage3DDepth; - property DeviceWithMaxLocalMemSize: TDCLDevice - read GetDeviceWithMaxLocalMemSize; - property DeviceWithMaxConstantArgs: TDCLDevice - read GetDeviceWithMaxConstantArgs; - property DeviceWithMaxConstantBufferSize: TDCLDevice - read GetDeviceWithMaxConstantBufferSize; - property DeviceWithMaxMemAllocSize: TDCLDevice - read GetDeviceWithMaxMemAllocSize; - property DeviceWithMaxParameterSize: TDCLDevice - read GetDeviceWithMaxParameterSize; - property DeviceWithMaxReadImageArgs: TDCLDevice - read GetDeviceWithMaxReadImageArgs; - property DeviceWithMaxSamplers: TDCLDevice read GetDeviceWithMaxSamplers; - property DeviceWithMaxWorkGroupSize: TDCLDevice - read GetDeviceWithMaxWorkGroupSize; - property DeviceWithMaxWorkItemDimensions: TDCLDevice - read GetDeviceWithMaxWorkItemDimensions; - property DeviceWithMaxWriteImageArgs: TDCLDevice - read GetDeviceWithMaxWriteImageArgs; - procedure Free(); - end; - - TDCLPlatforms = class - private - FPlatforms: Array of TDCLPlatform; - FPlatformCount: CL_uint; - FStatus: CL_int; - function GetPlatform(Index: CL_uint): TDCLPlatform; - public - constructor Create(); - property PlatformCount: CL_uint read FPlatformCount; - property Status: CL_int read FStatus; - property Platforms[Index: CL_uint]: TDCLPlatform read GetPlatform; - procedure Free(); - end; - -implementation - -function UpperCase(const S: AnsiString): AnsiString; -var - Ch: AnsiChar; - L: Integer; - Source, Dest: PAnsiChar; -begin - L := Length(S); - SetLength(Result, L); - Source := Pointer(S); - Dest := Pointer(Result); - while L <> 0 do - begin - Ch := Source^; - if (Ch >= 'a') and (Ch <= 'z') then - Dec(Ch, 32); - Dest^ := Ch; - Inc(Source); - Inc(Dest); - Dec(L); - end; -end; - -function IntToStr(Value: Integer): AnsiString; -begin - Str(Value, Result); -end; - -{ TDCLPlatforms } - -constructor TDCLPlatforms.Create; -var - Platforms: Array of PCL_platform_id; - i: Integer; -begin - FStatus := clGetPlatformIDs(0, nil, @FPlatformCount); - if FStatus = CL_SUCCESS then - begin - if FPlatformCount > 0 then - begin - SetLength(Platforms, FPlatformCount); - SetLength(FPlatforms, FPlatformCount); - FStatus := clGetPlatformIDs(FPlatformCount, @Platforms[0], nil); - for i := 0 to FPlatformCount - 1 do - begin - FPlatforms[i] := TDCLPlatform.Create(Platforms[i]); - end; - SetLength(Platforms, 0); - end; - end; -end; - -procedure TDCLPlatforms.Free; -var - i: Integer; -begin - for i := 0 to FPlatformCount - 1 do - begin - FPlatforms[i].Free(); - end; - SetLength(FPlatforms, 0); - inherited Free(); -end; - -function TDCLPlatforms.GetPlatform(Index: CL_uint): TDCLPlatform; -begin - if (Index < FPlatformCount) then - Result := FPlatforms[Index] - else - Result := nil; -end; - -{ TDCLPlatform } - -constructor TDCLPlatform.Create(Platform_id: PCL_platform_id); -var - Size: Size_t; - Devices: Array of PCL_device_id; - i, current, previous: Integer; - -begin - inherited Create(); - FPlatform_id := Platform_id; - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_PROFILE, 0, nil, Size); - SetLength(FProfile, Size); - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_PROFILE, Size, - @FProfile[1], Size); - // FProfile := Buffer; - - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_VERSION, 0, nil, Size); - SetLength(FVersion, Size); - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_VERSION, Size, - @FVersion[1], Size); - SetLength(FName, Size); - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_NAME, Size, - @FName[1], Size); - SetLength(FVendor, Size); - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_VENDOR, Size, - @FVendor[1], Size); - SetLength(FExtensionsString, Size); - FStatus := clGetPlatformInfo(FPlatform_id, CL_PLATFORM_EXTENSIONS, Size, - @FExtensionsString[1], Size); - FExtensionsCount := 0; - i := 1; - while (i <= Length(FExtensionsString)) do - begin - if ((FExtensionsString[i] = ' ') or (FExtensionsString[i] = #0)) then - Inc(FExtensionsCount); - Inc(i); - end; - SetLength(FExtensions, FExtensionsCount); - previous := 1; - current := 1; - i := 0; - while (current <= Length(FExtensionsString)) do - begin - if ((FExtensionsString[current] = ' ') or (FExtensionsString[current] = #0)) - then - begin - FExtensions[i] := UpperCase(Copy(FExtensionsString, previous, - current - previous - 1)); - previous := current + 1; - Inc(i); - end; - Inc(current); - end; - - FStatus := clGetDeviceIDs(FPlatform_id, CL_DEVICE_TYPE_ALL, 0, nil, - @FDeviceCount); - if FDeviceCount > 0 then - begin - SetLength(Devices, FDeviceCount); - FStatus := clGetDeviceIDs(FPlatform_id, CL_DEVICE_TYPE_ALL, FDeviceCount, - @Devices[0], nil); - SetLength(FDevices, FDeviceCount); - for i := 0 to FDeviceCount - 1 do - begin - FDevices[i] := TDCLDevice.Create(Devices[i]); - end; - end; - -end; - -procedure TDCLPlatform.Free; -var - i: Integer; -begin - SetLength(FExtensions, 0); - FExtensionsString := ''; - for i := 0 to FDeviceCount - 1 do - begin - FDevices[i].Free(); - end; - SetLength(FDevices, 0); - inherited Free(); -end; - -function TDCLPlatform.GetDevice(Index: CL_uint): TDCLDevice; -begin - if (Index < FDeviceCount) then - Result := FDevices[Index] - else - Result := nil; -end; - -function TDCLPlatform.GetDeviceWithMaxClockFrequency: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxClockFrequency; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxComputeUnits: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxComputeUnits; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxConstantArgs: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxConstantArgs; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxConstantBufferSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_ulong; - begin - Result := Device.MaxConstantBufferSize; - end; - -var - i: Integer; - MaxValue: CL_ulong; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxGlobalMemCacheLineSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.GlobalMemCacheLineSize; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxGlobalMemCacheSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_ulong; - begin - Result := Device.GlobalMemCacheSize; - end; - -var - i: Integer; - MaxValue: CL_ulong; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxGlobalMemSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_ulong; - begin - Result := Device.GlobalMemSize; - end; - -var - i: Integer; - MaxValue: CL_ulong; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxImage2DHeight: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.Image2DMaxHeight; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxImage2DWidth: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.Image2DMaxWidth; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxImage3DDepth: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.Image3DMaxDepth; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxImage3DHeight: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.Image3DMaxHeight; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxImage3DWidth: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.Image3DMaxWidth; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxLocalMemSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_ulong; - begin - Result := Device.LocalMemSize; - end; - -var - i: Integer; - MaxValue: CL_ulong; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxMemAllocSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_ulong; - begin - Result := Device.MaxMemAllocSize; - end; - -var - i: Integer; - MaxValue: CL_ulong; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxParameterSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.MaxParameterSize; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxReadImageArgs: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxReadImageArgs; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxSamplers: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxSamplers; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxWorkGroupSize: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): Size_t; - begin - Result := Device.MaxWorkGroupSize; - end; - -var - i: Integer; - MaxValue: Size_t; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxWorkItemDimensions: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxWorkItemDimensions; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetDeviceWithMaxWriteImageArgs: TDCLDevice; - function GetParameterDevice(const Device: TDCLDevice): CL_uint; - begin - Result := Device.MaxWriteImageArgs; - end; - -var - i: Integer; - MaxValue: CL_uint; - MaxValuePos: CL_uint; -begin - if FDeviceCount = 0 then - begin - Result := nil; - Exit; - end; - MaxValue := GetParameterDevice(FDevices[0]); - MaxValuePos := 0; - for i := 1 to FDeviceCount - 1 do - begin - if GetParameterDevice(FDevices[i]) > MaxValue then - begin - MaxValue := GetParameterDevice(FDevices[i]); - MaxValuePos := i; - end; - end; - Result := FDevices[MaxValuePos]; -end; - -function TDCLPlatform.GetExtensions(Index: Size_t): AnsiString; -begin - if Index < FExtensionsCount then - Result := FExtensions[Index] - else - Result := ''; -end; - -function TDCLPlatform.IsPresentExtension(const ExtensionName - : AnsiString): Boolean; -var - i: Integer; - UppName: AnsiString; -begin - Result := False; - UppName := UpperCase(ExtensionName); - for i := 0 to High(FExtensions) do - begin - if FExtensions[i] = UppName then - begin - Result := True; - Break; - end; - end; -end; - -{ TDCLDevice } - -constructor TDCLDevice.Create(Device_Id: PCL_device_id); -(* - need to add - CL_DEVICE_TYPE - CL_DEVICE_MAX_WORK_ITEM_SIZES - CL_DEVICE_SINGLE_FP_CONFIG - CL_DEVICE_GLOBAL_MEM_CACHE_TYPE - CL_DEVICE_GLOBAL_MEM_CACHE_TYPE - CL_DEVICE_EXECUTION_CAPABILITIES - CL_DEVICE_QUEUE_PROPERTIES -*) - -var - Size: Size_t; - device_type: CL_device_type; - b_bool: CL_bool; - - i, current, previous: Integer; -begin - inherited Create(); - FDevice_id := Device_Id; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NAME, 0, nil, Size); - SetLength(FName, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NAME, Size, @FName[1], Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_VENDOR, 0, nil, Size); - SetLength(FVendor, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_VENDOR, Size, - @FVendor[1], Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_VERSION, 0, nil, Size); - SetLength(FVersion, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_VERSION, Size, - @FVersion[1], Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PROFILE, 0, nil, Size); - SetLength(FProfile, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PROFILE, Size, - @FProfile[1], Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_TYPE_INFO, - SizeOf(device_type), @device_type, Size); - if (device_type and CL_DEVICE_TYPE_CPU) <> 0 then - FIsCPU := True; - if (device_type and CL_DEVICE_TYPE_GPU) <> 0 then - FIsGPU := True; - if (device_type and CL_DEVICE_TYPE_ACCELERATOR) <> 0 then - FIsAccelerator := True; - if (device_type and CL_DEVICE_TYPE_DEFAULT) <> 0 then - FIsDefault := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, - SizeOf(FMaxWorkGroupSize), @FMaxWorkGroupSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, - SizeOf(FNativeVectorPreferredChar), @FNativeVectorPreferredChar, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, - SizeOf(FNativeVectorPreferredShort), @FNativeVectorPreferredShort, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, - SizeOf(FNativeVectorPreferredInt), @FNativeVectorPreferredInt, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, - SizeOf(FNativeVectorPreferredLong), @FNativeVectorPreferredLong, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, - SizeOf(FNativeVectorPreferredFloat), @FNativeVectorPreferredFloat, Size); - FStatus := clGetDeviceInfo(FDevice_id, - CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, - SizeOf(FNativeVectorPreferredDouble), @FNativeVectorPreferredDouble, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, - SizeOf(FNativeVectorPreferredHalf), @FNativeVectorPreferredHalf, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, - SizeOf(FNativeVectorWidthChar), @FNativeVectorWidthChar, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, - SizeOf(FNativeVectorWidthShort), @FNativeVectorWidthShort, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, - SizeOf(FNativeVectorWidthInt), @FNativeVectorWidthInt, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, - SizeOf(FNativeVectorWidthLong), @FNativeVectorWidthLong, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, - SizeOf(FNativeVectorWidthFloat), @FNativeVectorWidthFloat, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, - SizeOf(FNativeVectorWidthDouble), @FNativeVectorWidthDouble, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, - SizeOf(FNativeVectorWidthHalf), @FNativeVectorWidthHalf, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_CLOCK_FREQUENCY, - SizeOf(FMaxClockFrequency), @FMaxClockFrequency, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_ADDRESS_BITS, - SizeOf(FAddressBits), @FAddressBits, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, - SizeOf(FMaxMemAllocSize), @FMaxMemAllocSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_IMAGE_SUPPORT, - SizeOf(b_bool), @b_bool, Size); - if b_bool <> 0 then - FIsImageSupport := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_READ_IMAGE_ARGS, - SizeOf(FMaxReadImageArgs), @FMaxReadImageArgs, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, - SizeOf(FMaxWriteImageArgs), @FMaxWriteImageArgs, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_IMAGE2D_MAX_WIDTH, - SizeOf(FImage2DMaxWidth), @FImage2DMaxWidth, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_IMAGE2D_MAX_HEIGHT, - SizeOf(FImage2DMaxHeight), @FImage2DMaxHeight, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_IMAGE3D_MAX_WIDTH, - SizeOf(FImage3DMaxWidth), @FImage3DMaxWidth, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_IMAGE3D_MAX_HEIGHT, - SizeOf(FImage3DMaxHeight), @FImage3DMaxHeight, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_IMAGE3D_MAX_DEPTH, - SizeOf(FImage3DMaxDepth), @FImage3DMaxDepth, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_SAMPLERS, - SizeOf(FMaxSamplers), @FMaxSamplers, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_PARAMETER_SIZE, - SizeOf(FMaxParameterSize), @FMaxParameterSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MEM_BASE_ADDR_ALIGN, - SizeOf(FMemBaseAddrAlign), @FMemBaseAddrAlign, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, - SizeOf(FMinDataTypeAlignSize), @FMinDataTypeAlignSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, - SizeOf(FGlobalMemCacheLineSize), @FGlobalMemCacheLineSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, - SizeOf(FGlobalMemCacheSize), @FGlobalMemCacheSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_GLOBAL_MEM_SIZE, - SizeOf(FGlobalMemSize), @FGlobalMemSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, - SizeOf(FMaxConstantBufferSize), @FMaxConstantBufferSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_CONSTANT_ARGS, - SizeOf(FMaxConstantArgs), @FMaxConstantArgs, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_LOCAL_MEM_SIZE, - SizeOf(FLocalMemSize), @FLocalMemSize, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_ENDIAN_LITTLE, - SizeOf(b_bool), @b_bool, Size); - if b_bool <> 0 then - FIsErrorCorrectionSupport := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_ENDIAN_LITTLE, - SizeOf(b_bool), @b_bool, Size); - if b_bool <> 0 then - FIsHostUnifiedMemory := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_PROFILING_TIMER_RESOLUTION, - SizeOf(FProfilingTimerResolution), @FProfilingTimerResolution, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_ENDIAN_LITTLE, - SizeOf(b_bool), @b_bool, Size); - if b_bool <> 0 then - FIsEndianLittle := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_AVAILABLE, SizeOf(b_bool), - @b_bool, Size); - if b_bool <> 0 then - FIsAvailable := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_COMPILER_AVAILABLE, - SizeOf(b_bool), @b_bool, Size); - if b_bool <> 0 then - FIsCompilerAvailable := True; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_VENDOR_ID, SizeOf(FVendorId), - @FVendorId, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_COMPUTE_UNITS, - SizeOf(FMaxComputeUnits), @FMaxComputeUnits, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, - SizeOf(FMaxWorkItemDimensions), @FMaxWorkItemDimensions, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_EXTENSIONS, 0, nil, Size); - SetLength(FExtensionsString, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_EXTENSIONS, Size, - @FExtensionsString[1], Size); - FExtensionsCount := 0; - i := 1; - while (i <= Length(FExtensionsString)) do - begin - if ((FExtensionsString[i] = ' ') or (FExtensionsString[i] = #0)) then - begin - if (i > 1) then - begin - if ((FExtensionsString[i - 1] <> ' ') and - (FExtensionsString[i - 1] <> #0)) then - begin - Inc(FExtensionsCount); - end; - end - else - Inc(FExtensionsCount); - end; - Inc(i); - end; - SetLength(FExtensions, FExtensionsCount); - previous := 1; - current := 1; - i := 0; - while (current <= Length(FExtensionsString)) do - begin - if ((FExtensionsString[current] = AnsiString(' ')) or - (FExtensionsString[current] = #0)) then - begin - if (current > previous) then - FExtensions[i] := UpperCase(Copy(FExtensionsString, previous, - current - previous - 1)); - previous := current + 1; - Inc(i); - end; - Inc(current); - end; - - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_OPENCL_C_VERSION, 0, - nil, Size); - SetLength(FOpenCLCVersion, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DEVICE_OPENCL_C_VERSION, Size, - @FOpenCLCVersion[1], Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DRIVER_VERSION, 0, nil, Size); - SetLength(FDriverVersion, Size); - FStatus := clGetDeviceInfo(FDevice_id, CL_DRIVER_VERSION, Size, - @FDriverVersion[1], Size); - FContext := TDCLContext.Create(FDevice_id); -end; - -function TDCLDevice.CreateBuffer(const Size: Size_t; const Data: Pointer; - const Flags: TDCLMemFlagsSet): TDCLBuffer; -begin - Result := TDCLBuffer.Create(Context.FContext, Flags, Size, Data); -end; - -function TDCLDevice.CreateCommandQueue(const Properties - : TDCLCommandQueuePropertiesSet): TDCLCommandQueue; -begin - Result := TDCLCommandQueue.Create(Device_Id, Context.FContext, Properties); -end; - -function TDCLDevice.CreateContext: TDCLContext; -begin - Result := TDCLContext.Create(FDevice_id); -end; - -function TDCLDevice.CreateProgram(const Source: PPChar; const Options: PPChar) - : TDCLProgram; -begin - Result := TDCLProgram.Create(FContext.FContext, Source, Options); -end; - -function TDCLDevice.CreateImage2D(const Format: PCL_image_format; - const Width, Height, RowPitch: Size_t; const Data: Pointer; - const Flags: TDCLMemFlagsSet): TDCLImage2D; -begin - Result := TDCLImage2D.Create(Context.FContext, Flags, Format, Width, Height, - RowPitch, Data); -end; - -function TDCLDevice.CreateProgram(const FileName: String; const Options: PPChar) - : TDCLProgram; -var - F: TextFile; - Source: String; - buf: String; -begin - AssignFile(F, FileName); - Reset(F); - Source := ''; - while not(EOF(F)) do - begin - Readln(F, buf); - Source := Source + buf + #10 + #13; - end; - CloseFile(F); - Result := CreateProgram(@PString(Source), Options); -end; - -procedure TDCLDevice.Free; -begin - FContext.Free(); - SetLength(FExtensions, 0); - FExtensionsString := ''; - inherited Free(); -end; - -function TDCLDevice.GetExtensions(const Index: Size_t): AnsiString; -begin - if Index < FExtensionsCount then - Result := FExtensions[Index] - else - Result := ''; -end; - -function TDCLDevice.IsPresentExtension(const ExtensionName: AnsiString) - : Boolean; -var - i: Integer; - UppName: AnsiString; -begin - Result := False; - UppName := UpperCase(ExtensionName); - for i := 0 to High(FExtensions) do - begin - if FExtensions[i] = UppName then - begin - Result := True; - Break; - end; - end; -end; - -{ TDCLContext } - -constructor TDCLContext.Create(Device_Id: PCL_device_id); -(* - CL_CONTEXT_REFERENCE_COUNT - CL_CONTEXT_DEVICES - CL_CONTEXT_PROPERTIES -*) -var - Size: Size_t; -begin - inherited Create(); - FContext := clCreateContext(nil, 1, @Device_Id, nil, nil, FStatus); - FStatus := clGetContextInfo(FContext, CL_CONTEXT_NUM_DEVICES, - SizeOf(FNumDevices), @FNumDevices, Size); -end; - -{ TDCLQueue } - -constructor TDCLCommandQueue.Create(const Device_Id: PCL_device_id; - const Context: PCL_context; const Properties: TDCLCommandQueuePropertiesSet); -var - props: CL_command_queue_properties; -begin - props := 0; - if cqpOutOfOrderExecModeEnable in Properties then - props := props or CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; - FCommandQueue := clCreateCommandQueue(Context, Device_Id, props, FStatus); - FProperties := Properties; -end; - -procedure TDCLContext.Free; -begin - FStatus := clReleaseContext(FContext); - inherited Free(); -end; - -{ TDCLBuffer } - -constructor TDCLBuffer.Create(const Context: PCL_context; - const Flags: TDCLMemFlagsSet; const Size: Size_t; const Data: Pointer = nil); -var - fgs: CL_mem_flags; -begin - inherited Create(); - fgs := 0; - if mfReadWrite in Flags then - fgs := fgs or CL_MEM_READ_WRITE; - if mfWriteOnly in Flags then - fgs := fgs or CL_MEM_WRITE_ONLY; - if mfReadOnly in Flags then - fgs := fgs or CL_MEM_READ_ONLY; - if mfUseHostPtr in Flags then - fgs := fgs or CL_MEM_USE_HOST_PTR; - if mfAllocHostPtr in Flags then - fgs := fgs or CL_MEM_ALLOC_HOST_PTR; - if mfCopyHostPtr in Flags then - fgs := fgs or CL_MEM_COPY_HOST_PTR; - FMem := clCreateBuffer(Context, fgs, Size, Data, FStatus); - FSize := Size; -end; - -procedure TDCLBuffer.Free; -begin - FStatus := clReleaseMemObject(FMem); - inherited Free; -end; - -procedure TDCLCommandQueue.Execute(const Kernel: TDCLKernel; - const Size: Size_t); -begin - FStatus := clEnqueueNDRangeKernel(FCommandQueue, Kernel.FKernel, 1, nil, - @Size, nil, 0, nil, nil); - FStatus := clFinish(FCommandQueue); -end; - -procedure TDCLCommandQueue.Execute(const Kernel: TDCLKernel; - // const Device: PCL_device_id; - const Size: array of Size_t); -// var -// kernel2DWorkGroupSize: Size_t; -begin - // FStatus := clGetKernelWorkGroupInfo(Kernel.FKernel, Device, CL_KERNEL_WORK_GROUP_SIZE, SizeOf(Size_t), @kernel2DWorkGroupSize, nil); - FStatus := clEnqueueNDRangeKernel(FCommandQueue, Kernel.FKernel, Length(Size), - nil, @Size[0], nil, 0, nil, nil); - FStatus := clFinish(FCommandQueue); -end; - -procedure TDCLCommandQueue.Free; -begin - FStatus := clReleaseCommandQueue(FCommandQueue); - inherited Free(); -end; - -{ TDCLProgram } - -constructor TDCLProgram.Create(const Context: PCL_context; const Source: PPChar; - const Options: PPChar); -var - Size: Size_t; - // FBinaries: Array of Char; -begin - FProgram := clCreateProgramWithSource(Context, 1, Source, nil, FStatus); - FStatus := clBuildProgram(FProgram, 0, nil, Options^, nil, nil); - FStatus := clGetProgramInfo(FProgram, CL_PROGRAM_SOURCE, 0, nil, Size); - FSource := GetMemory(Size); - FStatus := clGetProgramInfo(FProgram, CL_PROGRAM_SOURCE, Size, FSource, Size); - FStatus := clGetProgramInfo(FProgram, CL_PROGRAM_BINARY_SIZES, 0, nil, - FBinarySizesCount); - SetLength(FBinarySizes, FBinarySizesCount); - FStatus := clGetProgramInfo(FProgram, CL_PROGRAM_BINARY_SIZES, - SizeOf(FBinarySizes), @FBinarySizes[0], FBinarySizesCount); - (* //Not yet - FStatus := clGetProgramInfo(FProgram,CL_PROGRAM_BINARIES,0,nil,@Size); - SetLength(FBinaries,Size); - FStatus := clGetProgramInfo(FProgram,CL_PROGRAM_BINARIES,Size,@FBinaries[0],nil); - Writeln(String(FBinaries)); - *) - -end; - -function TDCLProgram.CreateKernel(const KernelName: PPChar): TDCLKernel; -begin - Result := TDCLKernel.Create(FProgram, KernelName); -end; - -procedure TDCLProgram.Free; -begin - FStatus := clReleaseProgram(FProgram); - FSource := ''; - SetLength(FBinarySizes, 0); - inherited Free; -end; - -function TDCLProgram.GetBinarySizes(const Index: Size_t): Size_t; -begin - if (Index < FBinarySizesCount) then - Result := FBinarySizes[Index] - else - Result := 0; -end; - -{ TDCLKernel } - -constructor TDCLKernel.Create(const Program_: PCL_program; - const KernelName: PPChar); -begin - FKernel := clCreateKernel(Program_, KernelName^, FStatus); -end; - -procedure TDCLKernel.Free; -begin - FStatus := clReleaseKernel(FKernel); - inherited Free(); -end; - -function TDCLKernel.GetFunctionName: AnsiString; -var - Size: Size_t; - Buffer: Array of AnsiChar; -begin - FStatus := clGetKernelInfo(FKernel, CL_KERNEL_FUNCTION_NAME, 0, nil, Size); - SetLength(Buffer, Size); - FStatus := clGetKernelInfo(FKernel, CL_KERNEL_FUNCTION_NAME, Size, - @Buffer[0], Size); - Result := AnsiString(Buffer); - SetLength(Buffer, 0); -end; - -function TDCLKernel.GetNumArgs: CL_uint; -var - Size: Size_t; -begin - FStatus := clGetKernelInfo(FKernel, CL_KERNEL_NUM_ARGS, SizeOf(Result), - @Result, Size); -end; - -procedure TDCLKernel.SetArg(const Index: CL_uint; const Size: Size_t; - const Value: Pointer); -begin - FStatus := clSetKernelArg(FKernel, Index, Size, Value); -end; - -procedure TDCLKernel.SetArg(const Index: CL_uint; const Value: TDCLBuffer); -begin - SetArg(Index, SizeOf(@Value.FMem), @Value.FMem); -end; - -procedure TDCLKernel.SetArg(const Index: CL_uint; const Value: TDCLImage2D); -begin - SetArg(Index, SizeOf(@Value.FMem), @Value.FMem); -end; - -procedure TDCLCommandQueue.ReadBuffer(const Buffer: TDCLBuffer; - const Size: Size_t; const Data: Pointer); -begin - FStatus := clEnqueueReadBuffer(FCommandQueue, Buffer.FMem, CL_TRUE, 0, Size, - Data, 0, nil, nil); - clFinish(FCommandQueue); -end; - -procedure TDCLCommandQueue.ReadImage2D(const Image: TDCLImage2D; - const Width, Height: Size_t; const Data: Pointer); -var - origin, region: Array [0 .. 2] of Size_t; -begin - ZeroMemory(@origin, SizeOf(origin)); - region[0] := Width; - region[1] := Height; - region[2] := 1; // Image 2D - FStatus := clEnqueueReadImage(FCommandQueue, Image.FMem, CL_TRUE, @origin, - @region, 0, 0, Data, 0, nil, nil); - FStatus := clFinish(FCommandQueue); -end; - -procedure TDCLCommandQueue.WriteImage2D(const Image: TDCLImage2D; - const Width, Height: Size_t; const Data: Pointer); -var - origin, region: Array [0 .. 2] of Size_t; -begin - ZeroMemory(@origin, SizeOf(origin)); - region[0] := Width; - region[1] := Height; - region[2] := 1; // Image 2D - FStatus := clEnqueueWriteImage(FCommandQueue, Image.FMem, CL_TRUE, @origin, - @region, 0, 0, Data, 0, nil, nil); - FStatus := clFinish(FCommandQueue); -end; - -procedure TDCLCommandQueue.WriteBuffer(const Buffer: TDCLBuffer; - const Size: Size_t; const Data: Pointer); -begin - FStatus := clEnqueueWriteBuffer(FCommandQueue, Buffer.FMem, CL_TRUE, 0, Size, - Data, 0, nil, nil); -end; - -{ TDCLImage2D } - -constructor TDCLImage2D.Create(const Context: PCL_context; - const Flags: TDCLMemFlagsSet; const Format: PCL_image_format; - const Width, Height, RowPitch: Size_t; const Data: Pointer); -var - fgs: CL_mem_flags; -begin - inherited Create(); - fgs := 0; - if mfReadWrite in Flags then - fgs := fgs or CL_MEM_READ_WRITE; - if mfWriteOnly in Flags then - fgs := fgs or CL_MEM_WRITE_ONLY; - if mfReadOnly in Flags then - fgs := fgs or CL_MEM_READ_ONLY; - if mfUseHostPtr in Flags then - fgs := fgs or CL_MEM_USE_HOST_PTR; - if mfAllocHostPtr in Flags then - fgs := fgs or CL_MEM_ALLOC_HOST_PTR; - if mfCopyHostPtr in Flags then - fgs := fgs or CL_MEM_COPY_HOST_PTR; - FFormat := Format^; - FMem := clCreateImage2D(Context, fgs, @FFormat, Width, Height, RowPitch, - Data, FStatus); -end; - -procedure TDCLImage2D.Free; -begin - FStatus := clReleaseMemObject(FMem); - inherited Free(); -end; - -end. diff --git a/contrib/opencl/OpenCL.pas b/contrib/opencl/OpenCL.pas deleted file mode 100644 index e1413fb..0000000 --- a/contrib/opencl/OpenCL.pas +++ /dev/null @@ -1,1032 +0,0 @@ -(* ****************************************************************************** - * Copyright (c) 2008-2009 The Khronos Group Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and/or associated documentation files (the - * "Materials"), to deal in the Materials without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Materials, and to - * permit persons to whom the Materials are furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Materials. - * - * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. - ***************************************************************************** *) - -// ported to FPC by Dmitry 'skalogryz' Boyarintsev: 28th apr 2009 -// due to name conflict with type names, some constants have been renamed - -// Original C name Ported_name -// CL_DEVICE_TYPE CL_DEVICE_TYPE_INFO -// CL_DEVICE_LOCAL_MEM_TYPE CL_DEVICE_LOCAL_MEM_TYPE_INFO -// CL_CONTEXT_PROPERTIES CL_CONTEXT_PROPERTIES_INFO -// CL_CONTEXT_PLATFORM CL_CONTEXT_PLATFORM_INFO -// CL_FLOAT CL_FLOAT_TYPE -// CL_MEM_FLAGS CL_MEM_FLAGS_INFO -// CL_IMAGE_FORMAT CL_IMAGE_FORMAT_INFO - -unit OpenCL; - -interface - -uses - Windows; - -const - OpenCLlib = 'OpenCL.dll'; - - { cl_platform.h } - -const - CL_PLATFORM_NVIDIA = $3001; // NVidia specific platform value - - { * scalar types * } - -type - cl_char = int8; - cl_uchar = uint8; - cl_short = int16; - cl_ushort = uint16; - cl_int = int32; - cl_uint = uint32; - cl_long = int64; - cl_ulong = uint64; - - cl_half = uint16; - cl_float = single; - cl_double = double; - - Pcl_char = ^cl_char; - Pcl_uchar = ^cl_uchar; - Pcl_short = ^cl_short; - Pcl_ushort = ^cl_ushort; - Pcl_int = ^cl_int; - Pcl_uint = ^cl_uint; - Pcl_long = ^cl_long; - Pcl_ulong = ^cl_ulong; - - Pcl_half = ^cl_half; - Pcl_float = ^cl_float; - Pcl_double = ^cl_double; - -const - CL_CHAR_BIT = 8; - CL_SCHAR_MAX = 127; - CL_SCHAR_MIN = (-127 - 1); - CL_CHAR_MAX = CL_SCHAR_MAX; - CL_CHAR_MIN = CL_SCHAR_MIN; - CL_UCHAR_MAX = 255; - CL_SHRT_MAX = 32767; - CL_SHRT_MIN = (-32767 - 1); - CL_USHRT_MAX = 65535; - CL_INT_MAX = 2147483647; - CL_INT_MIN = (-2147483647 - 1); - CL_UINT_MAX = $FFFFFFFF; - CL_LONG_MAX = $7FFFFFFFFFFFFFFF; - CL_LONG_MIN = -$7FFFFFFFFFFFFFFF - 1; - CL_ULONG_MAX = $FFFFFFFFFFFFFFFF; - - CL_FLT_DIG = 6; - CL_FLT_MANT_DIG = 24; - CL_FLT_MAX_10_EXP = +38; - CL_FLT_MAX_EXP = +128; - CL_FLT_MIN_10_EXP = -37; - CL_FLT_MIN_EXP = -125; - CL_FLT_RADIX = 2; - // CL_FLT_MAX = 0x1.fffffep127f; - // CL_FLT_MIN = 0x1.0p-126f; - // CL_FLT_EPSILON = 0x1.0p-23f; - - CL_DBL_DIG = 15; - CL_DBL_MANT_DIG = 53; - CL_DBL_MAX_10_EXP = +308; - CL_DBL_MAX_EXP = +1024; - CL_DBL_MIN_10_EXP = -307; - CL_DBL_MIN_EXP = -1021; - CL_DBL_RADIX = 2; - // CL_DBL_MAX 0x1.fffffffffffffp1023 - // CL_DBL_MIN 0x1.0p-1022 - // CL_DBL_EPSILON 0x1.0p-52 - - { * - * Vector types - * - * Note: OpenCL requires that all types be naturally aligned. - * This means that vector types must be naturally aligned. - * For example, a vector of four floats must be aligned to - * a 16 byte boundary (calculated as 4 * the natural 4-byte - * alignment of the float). The alignment qualifiers here - * will only function properly if your compiler supports them - * and if you don't actively work to defeat them. For example, - * in order for a cl_float4 to be 16 byte aligned in a struct, - * the start of the struct must itself be 16-byte aligned. - * - * Maintaining proper alignment is the user's responsibility. - * } -type - cl_char2 = array [0 .. 1] of int8; - cl_char4 = array [0 .. 3] of int8; - cl_char8 = array [0 .. 7] of int8; - cl_char16 = array [0 .. 15] of int8; - - cl_uchar2 = array [0 .. 1] of uint8; - cl_uchar4 = array [0 .. 3] of uint8; - cl_uchar8 = array [0 .. 7] of uint8; - cl_uchar16 = array [0 .. 15] of uint8; - - cl_short2 = array [0 .. 1] of int16; - cl_short4 = array [0 .. 3] of int16; - cl_short8 = array [0 .. 7] of int16; - cl_short16 = array [0 .. 15] of int16; - - cl_ushort2 = array [0 .. 1] of uint16; - cl_ushort4 = array [0 .. 3] of uint16; - cl_ushort8 = array [0 .. 7] of uint16; - cl_ushort16 = array [0 .. 15] of uint16; - - cl_int2 = array [0 .. 1] of int32; - cl_int4 = array [0 .. 3] of int32; - cl_int8 = array [0 .. 7] of int32; - cl_int16 = array [0 .. 15] of int32; - - cl_uint2 = array [0 .. 1] of uint32; - cl_uint4 = array [0 .. 3] of uint32; - cl_uint8 = array [0 .. 7] of uint32; - cl_uint16 = array [0 .. 15] of uint32; - - cl_long2 = array [0 .. 1] of int64; - cl_long4 = array [0 .. 3] of int64; - cl_long8 = array [0 .. 7] of int64; - cl_long16 = array [0 .. 15] of int64; - - cl_ulong2 = array [0 .. 1] of uint64; - cl_ulong4 = array [0 .. 3] of uint64; - cl_ulong8 = array [0 .. 7] of uint64; - cl_ulong16 = array [0 .. 15] of uint64; - - cl_float2 = array [0 .. 1] of single; - cl_float4 = array [0 .. 3] of single; - cl_float8 = array [0 .. 7] of single; - cl_float16 = array [0 .. 15] of single; - - cl_double2 = array [0 .. 1] of double; - cl_double4 = array [0 .. 3] of double; - cl_double8 = array [0 .. 7] of double; - cl_double16 = array [0 .. 15] of double; - - { * There are no vector types for half * } - - // **************************************************************************** - - { cl.h } - -type - _cl_platform_id = record - end; - - _cl_device_id = record - end; - - _cl_context = record - end; - - _cl_command_queue = record - end; - - _cl_mem = record - end; - - _cl_program = record - end; - - _cl_kernel = record - end; - - _cl_event = record - end; - - _cl_sampler = record - end; - - cl_platform_id = ^_cl_platform_id; - cl_device_id = ^_cl_device_id; - cl_context = ^_cl_context; - cl_command_queue = ^_cl_command_queue; - cl_mem = ^_cl_mem; - cl_program = ^_cl_program; - cl_kernel = ^_cl_kernel; - cl_event = ^_cl_event; - cl_sampler = ^_cl_sampler; - - Pcl_platform_id = cl_platform_id; - Pcl_device_id = cl_device_id; - Pcl_context = cl_context; - Pcl_command_queue = cl_command_queue; - Pcl_mem = cl_mem; - Pcl_program = cl_program; - Pcl_kernel = cl_kernel; - Pcl_event = cl_event; - Pcl_sampler = cl_sampler; - - cl_bool = cl_uint; - // WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. - cl_bitfield = cl_ulong; - cl_device_type = cl_bitfield; - cl_platform_info = cl_uint; - cl_device_info = cl_uint; - cl_device_address_info = cl_bitfield; - cl_device_fp_config = cl_bitfield; - cl_device_mem_cache_type = cl_uint; - cl_device_local_mem_type = cl_uint; - cl_device_exec_capabilities = cl_bitfield; - cl_command_queue_properties = cl_bitfield; - - cl_context_properties = intptr; - cl_context_info = cl_uint; - cl_command_queue_info = cl_uint; - cl_channel_order = cl_uint; - cl_channel_type = cl_uint; - cl_mem_flags = cl_bitfield; - cl_mem_object_type = cl_uint; - cl_mem_info = cl_uint; - cl_image_info = cl_uint; - cl_addressing_mode = cl_uint; - cl_filter_mode = cl_uint; - cl_sampler_info = cl_uint; - cl_map_flags = cl_bitfield; - cl_program_info = cl_uint; - cl_program_build_info = cl_uint; - cl_build_status = cl_int; - cl_kernel_info = cl_uint; - cl_kernel_work_group_info = cl_uint; - cl_event_info = cl_uint; - cl_command_type = cl_uint; - cl_profiling_info = cl_uint; - - _cl_image_format = packed record - image_channel_order: cl_channel_order; - image_channel_data_type: cl_channel_type; - end; - - cl_image_format = _cl_image_format; - - Pcl_context_properties = ^cl_context_properties; - Pcl_image_format = ^cl_image_format; - -const - // Error Codes - CL_SUCCESS = 0; - CL_DEVICE_NOT_FOUND = -1; - CL_DEVICE_NOT_AVAILABLE = -2; - CL_DEVICE_COMPILER_NOT_AVAILABLE = -3; - CL_MEM_OBJECT_ALLOCATION_FAILURE = -4; - CL_OUT_OF_RESOURCES = -5; - CL_OUT_OF_HOST_MEMORY = -6; - CL_PROFILING_INFO_NOT_AVAILABLE = -7; - CL_MEM_COPY_OVERLAP = -8; - CL_IMAGE_FORMAT_MISMATCH = -9; - CL_IMAGE_FORMAT_NOT_SUPPORTED = -10; - CL_BUILD_PROGRAM_FAILURE = -11; - CL_MAP_FAILURE = -12; - - CL_INVALID_VALUE = -30; - CL_INVALID_DEVICE_TYPE = -31; - CL_INVALID_PLATFORM = -32; - CL_INVALID_DEVICE = -33; - CL_INVALID_CONTEXT = -34; - CL_INVALID_QUEUE_PROPERTIES = -35; - CL_INVALID_COMMAND_QUEUE = -36; - CL_INVALID_HOST_PTR = -37; - CL_INVALID_MEM_OBJECT = -38; - CL_INVALID_IMAGE_FORMAT_DESCRIPTOR = -39; - CL_INVALID_IMAGE_SIZE = -40; - CL_INVALID_SAMPLER = -41; - CL_INVALID_BINARY = -42; - CL_INVALID_BUILD_OPTIONS = -43; - CL_INVALID_PROGRAM = -44; - CL_INVALID_PROGRAM_EXECUTABLE = -45; - CL_INVALID_KERNEL_NAME = -46; - CL_INVALID_KERNEL_DEFINITION = -47; - CL_INVALID_KERNEL = -48; - CL_INVALID_ARG_INDEX = -49; - CL_INVALID_ARG_VALUE = -50; - CL_INVALID_ARG_SIZE = -51; - CL_INVALID_KERNEL_ARGS = -52; - CL_INVALID_WORK_DIMENSION = -53; - CL_INVALID_WORK_GROUP_SIZE = -54; - CL_INVALID_WORK_ITEM_SIZE = -55; - CL_INVALID_GLOBAL_OFFSET = -56; - CL_INVALID_EVENT_WAIT_LIST = -57; - CL_INVALID_EVENT = -58; - CL_INVALID_OPERATION = -59; - CL_INVALID_GL_OBJECT = -60; - CL_INVALID_BUFFER_SIZE = -61; - CL_INVALID_MIP_LEVEL = -62; - - // OpenCL Version - CL_VERSION_1_0 = 1; - - // cl_bool - CL_FALSE = 0; - CL_TRUE = 1; - - // cl_platform_info - CL_PLATFORM_PROFILE = $0900; - CL_PLATFORM_VERSION = $0901; - CL_PLATFORM_NAME = $0902; - CL_PLATFORM_VENDOR = $0903; - CL_PLATFORM_EXTENSIONS = $0904; - - // cl_device_type - bitfield - CL_DEVICE_TYPE_DEFAULT = (1 shl 0); - CL_DEVICE_TYPE_CPU = (1 shl 1); - CL_DEVICE_TYPE_GPU = (1 shl 2); - CL_DEVICE_TYPE_ACCELERATOR = (1 shl 3); - CL_DEVICE_TYPE_ALL = $FFFFFFFF; - - // cl_device_info - CL_DEVICE_TYPE_INFO = $1000; // CL_DEVICE_TYPE - CL_DEVICE_VENDOR_ID = $1001; - CL_DEVICE_MAX_COMPUTE_UNITS = $1002; - CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS = $1003; - CL_DEVICE_MAX_WORK_GROUP_SIZE = $1004; - CL_DEVICE_MAX_WORK_ITEM_SIZES = $1005; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR = $1006; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT = $1007; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT = $1008; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG = $1009; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT = $100A; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE = $100B; - CL_DEVICE_MAX_CLOCK_FREQUENCY = $100C; - CL_DEVICE_ADDRESS_BITS = $100D; - CL_DEVICE_MAX_READ_IMAGE_ARGS = $100E; - CL_DEVICE_MAX_WRITE_IMAGE_ARGS = $100F; - CL_DEVICE_MAX_MEM_ALLOC_SIZE = $1010; - CL_DEVICE_IMAGE2D_MAX_WIDTH = $1011; - CL_DEVICE_IMAGE2D_MAX_HEIGHT = $1012; - CL_DEVICE_IMAGE3D_MAX_WIDTH = $1013; - CL_DEVICE_IMAGE3D_MAX_HEIGHT = $1014; - CL_DEVICE_IMAGE3D_MAX_DEPTH = $1015; - CL_DEVICE_IMAGE_SUPPORT = $1016; - CL_DEVICE_MAX_PARAMETER_SIZE = $1017; - CL_DEVICE_MAX_SAMPLERS = $1018; - CL_DEVICE_MEM_BASE_ADDR_ALIGN = $1019; - CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE = $101A; - CL_DEVICE_SINGLE_FP_CONFIG = $101B; - CL_DEVICE_DOUBLE_FP_CONFIG = $1032; - CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF = $1034; - CL_DEVICE_HOST_UNIFIED_MEMORY = $1035; - CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR = $1036; - CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT = $1037; - CL_DEVICE_NATIVE_VECTOR_WIDTH_INT = $1038; - CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG = $1039; - CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT = $103A; - CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE = $103B; - CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF = $103C; - CL_DEVICE_OPENCL_C_VERSION = $103D; - CL_DEVICE_LINKER_AVAILABLE = $103E; - CL_DEVICE_BUILT_IN_KERNELS = $103F; - CL_DEVICE_IMAGE_MAX_BUFFER_SIZE = $1040; - CL_DEVICE_IMAGE_MAX_ARRAY_SIZE = $1041; - CL_DEVICE_PARENT_DEVICE = $1042; - CL_DEVICE_PARTITION_MAX_SUB_DEVICES = $1043; - CL_DEVICE_PARTITION_PROPERTIES = $1044; - CL_DEVICE_PARTITION_AFFINITY_DOMAIN = $1045; - CL_DEVICE_PARTITION_TYPE = $1046; - CL_DEVICE_REFERENCE_COUNT = $1047; - CL_DEVICE_PREFERRED_INTEROP_USER_SYNC = $1048; - CL_DEVICE_PRINTF_BUFFER_SIZE = $1049; - CL_DEVICE_GLOBAL_MEM_CACHE_TYPE = $101C; - CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE = $101D; - CL_DEVICE_GLOBAL_MEM_CACHE_SIZE = $101E; - CL_DEVICE_GLOBAL_MEM_SIZE = $101F; - CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE = $1020; - CL_DEVICE_MAX_CONSTANT_ARGS = $1021; - CL_DEVICE_LOCAL_MEM_TYPE_INFO = $1022; // CL_DEVICE_LOCAL_MEM_TYPE - CL_DEVICE_LOCAL_MEM_SIZE = $1023; - CL_DEVICE_ERROR_CORRECTION_SUPPORT = $1024; - CL_DEVICE_PROFILING_TIMER_RESOLUTION = $1025; - CL_DEVICE_ENDIAN_LITTLE = $1026; - CL_DEVICE_AVAILABLE = $1027; - CL_DEVICE_COMPILER_AVAILABLE = $1028; - CL_DEVICE_EXECUTION_CAPABILITIES = $1029; - CL_DEVICE_QUEUE_PROPERTIES = $102A; - CL_DEVICE_NAME = $102B; - CL_DEVICE_VENDOR = $102C; - CL_DRIVER_VERSION = $102D; - CL_DEVICE_PROFILE = $102E; - CL_DEVICE_VERSION = $102F; - CL_DEVICE_EXTENSIONS = $1030; - CL_DEVICE_PLATFORM = $1031; - - // cl_device_address_info - bitfield - CL_DEVICE_ADDRESS_32_BITS = (1 shl 0); - CL_DEVICE_ADDRESS_64_BITS = (1 shl 1); - - // cl_device_fp_config - bitfield - CL_FP_DENORM = (1 shl 0); - CL_FP_INF_NAN = (1 shl 1); - CL_FP_ROUND_TO_NEAREST = (1 shl 2); - CL_FP_ROUND_TO_ZERO = (1 shl 3); - CL_FP_ROUND_TO_INF = (1 shl 4); - CL_FP_FMA = (1 shl 5); - - // cl_device_mem_cache_type - CL_NONE = $0; - CL_READ_ONLY_CACHE = $1; - CL_READ_WRITE_CACHE = $2; - - // cl_device_local_mem_type - CL_LOCAL = $1; - CL_GLOBAL = $2; - - // cl_device_exec_capabilities - bitfield - CL_EXEC_KERNEL = (1 shl 0); - CL_EXEC_NATIVE_KERNEL = (1 shl 1); - - // cl_command_queue_properties - bitfield - CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE = (1 shl 0); - CL_QUEUE_PROFILING_ENABLE = (1 shl 1); - - // cl_context_info - CL_CONTEXT_REFERENCE_COUNT = $1080; - CL_CONTEXT_DEVICES = $1081; - CL_CONTEXT_PROPERTIES_INFO = $1082; // CL_CONTEXT_PROPERTIES - CL_CONTEXT_NUM_DEVICES = $1083; - CL_CONTEXT_PLATFORM_INFO = $1084; // CL_CONTEXT_PLATFORM - - // cl_command_queue_info - CL_QUEUE_CONTEXT = $1090; - CL_QUEUE_DEVICE = $1091; - CL_QUEUE_REFERENCE_COUNT = $1092; - CL_QUEUE_PROPERTIES = $1093; - - // cl_mem_flags - bitfield - CL_MEM_READ_WRITE = (1 shl 0); - CL_MEM_WRITE_ONLY = (1 shl 1); - CL_MEM_READ_ONLY = (1 shl 2); - CL_MEM_USE_HOST_PTR = (1 shl 3); - CL_MEM_ALLOC_HOST_PTR = (1 shl 4); - CL_MEM_COPY_HOST_PTR = (1 shl 5); - - // cl_channel_order - CL_R = $10B0; - CL_A = $10B1; - CL_RG = $10B2; - CL_RA = $10B3; - CL_RGB = $10B4; - CL_RGBA = $10B5; - CL_BGRA = $10B6; - CL_ARGB = $10B7; - CL_INTENSITY = $10B8; - CL_LUMINANCE = $10B9; - - // cl_channel_type - CL_SNORM_INT8 = $10D0; - CL_SNORM_INT16 = $10D1; - CL_UNORM_INT8 = $10D2; - CL_UNORM_INT16 = $10D3; - CL_UNORM_SHORT_565 = $10D4; - CL_UNORM_SHORT_555 = $10D5; - CL_UNORM_INT_101010 = $10D6; - CL_SIGNED_INT8 = $10D7; - CL_SIGNED_INT16 = $10D8; - CL_SIGNED_INT32 = $10D9; - CL_UNSIGNED_INT8 = $10DA; - CL_UNSIGNED_INT16 = $10DB; - CL_UNSIGNED_INT32 = $10DC; - CL_HALF_FLOAT = $10DD; - CL_FLOAT_TYPE = $10DE; // CL_FLOAT - - // cl_mem_object_type - CL_MEM_OBJECT_BUFFER = $10F0; - CL_MEM_OBJECT_IMAGE2D = $10F1; - CL_MEM_OBJECT_IMAGE3D = $10F2; - - // cl_mem_info - CL_MEM_TYPE = $1100; - CL_MEM_FLAGS_INFO = $1101; // CL_MEM_FLAGS - CL_MEM_SIZE = $1102; - CL_MEM_HOST_PTR = $1103; - CL_MEM_MAP_COUNT = $1104; - CL_MEM_REFERENCE_COUNT = $1105; - CL_MEM_CONTEXT = $1106; - - // cl_image_info - CL_IMAGE_FORMAT_INFO = $1110; // CL_IMAGE_FORMAT - CL_IMAGE_ELEMENT_SIZE = $1111; - CL_IMAGE_ROW_PITCH = $1112; - CL_IMAGE_SLICE_PITCH = $1113; - CL_IMAGE_WIDTH = $1114; - CL_IMAGE_HEIGHT = $1115; - CL_IMAGE_DEPTH = $1116; - - // cl_addressing_mode - CL_ADDRESS_NONE = $1130; - CL_ADDRESS_CLAMP_TO_EDGE = $1131; - CL_ADDRESS_CLAMP = $1132; - CL_ADDRESS_REPEAT = $1133; - - // cl_filter_mode - CL_FILTER_NEAREST = $1140; - CL_FILTER_LINEAR = $1141; - - // cl_sampler_info - CL_SAMPLER_REFERENCE_COUNT = $1150; - CL_SAMPLER_CONTEXT = $1151; - CL_SAMPLER_NORMALIZED_COORDS = $1152; - CL_SAMPLER_ADDRESSING_MODE = $1153; - CL_SAMPLER_FILTER_MODE = $1154; - - // cl_map_flags - bitfield - CL_MAP_READ = (1 shl 0); - CL_MAP_WRITE = (1 shl 1); - - // cl_program_info - CL_PROGRAM_REFERENCE_COUNT = $1160; - CL_PROGRAM_CONTEXT = $1161; - CL_PROGRAM_NUM_DEVICES = $1162; - CL_PROGRAM_DEVICES = $1163; - CL_PROGRAM_SOURCE = $1164; - CL_PROGRAM_BINARY_SIZES = $1165; - CL_PROGRAM_BINARIES = $1166; - - // cl_program_build_info - CL_PROGRAM_BUILD_STATUS = $1181; - CL_PROGRAM_BUILD_OPTIONS = $1182; - CL_PROGRAM_BUILD_LOG = $1183; - - // cl_build_status - CL_BUILD_SUCCESS = 0; - CL_BUILD_NONE = -1; - CL_BUILD_ERROR = -2; - CL_BUILD_IN_PROGRESS = -3; - - // cl_kernel_info - CL_KERNEL_FUNCTION_NAME = $1190; - CL_KERNEL_NUM_ARGS = $1191; - CL_KERNEL_REFERENCE_COUNT = $1192; - CL_KERNEL_CONTEXT = $1193; - CL_KERNEL_PROGRAM = $1194; - - // cl_kernel_work_group_info - CL_KERNEL_WORK_GROUP_SIZE = $11B0; - CL_KERNEL_COMPILE_WORK_GROUP_SIZE = $11B1; - CL_KERNEL_LOCAL_MEM_SIZE = $11B2; - - // cl_event_info - CL_EVENT_COMMAND_QUEUE = $11D0; - CL_EVENT_COMMAND_TYPE = $11D1; - CL_EVENT_REFERENCE_COUNT = $11D2; - CL_EVENT_COMMAND_EXECUTION_STATUS = $11D3; - - // cl_command_type - CL_COMMAND_NDRANGE_KERNEL = $11F0; - CL_COMMAND_TASK = $11F1; - CL_COMMAND_NATIVE_KERNEL = $11F2; - CL_COMMAND_READ_BUFFER = $11F3; - CL_COMMAND_WRITE_BUFFER = $11F4; - CL_COMMAND_COPY_BUFFER = $11F5; - CL_COMMAND_READ_IMAGE = $11F6; - CL_COMMAND_WRITE_IMAGE = $11F7; - CL_COMMAND_COPY_IMAGE = $11F8; - CL_COMMAND_COPY_IMAGE_TO_BUFFER = $11F9; - CL_COMMAND_COPY_BUFFER_TO_IMAGE = $11FA; - CL_COMMAND_MAP_BUFFER = $11FB; - CL_COMMAND_MAP_IMAGE = $11FC; - CL_COMMAND_UNMAP_MEM_OBJECT = $11FD; - CL_COMMAND_MARKER = $11FE; - CL_COMMAND_WAIT_FOR_EVENTS = $11FF; - CL_COMMAND_BARRIER = $1200; - CL_COMMAND_ACQUIRE_GL_OBJECTS = $1201; - CL_COMMAND_RELEASE_GL_OBJECTS = $1202; - - // command execution status - CL_COMPLETE = $0; - CL_RUNNING = $1; - CL_SUBMITTED = $2; - CL_QUEUED = $3; - - // cl_profiling_info - CL_PROFILING_COMMAND_QUEUED = $1280; - CL_PROFILING_COMMAND_SUBMIT = $1281; - CL_PROFILING_COMMAND_START = $1282; - CL_PROFILING_COMMAND_END = $1283; - - // **************************************************************************** - - // Platform APIs -function clGetPlatformIDs(num_entries: cl_uint; platforms: Pcl_platform_id; - num_platforms: Pcl_uint): cl_int; stdcall; - external OpenCLlib name 'clGetPlatformIDs'; - -function clGetPlatformInfo(_platform: cl_platform_id; - param_name: cl_platform_info; value_size: size_t; value: Pointer; - var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetPlatformInfo'; - -// Device APIs -function clGetDeviceIDs(_platform: cl_platform_id; device_type: cl_device_type; - num_entries: cl_uint; devices: Pcl_device_id; num_devices: Pcl_uint): cl_int; - stdcall; external OpenCLlib name 'clGetDeviceIDs'; - -function clGetDeviceInfo(device: cl_device_id; param_name: cl_device_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetDeviceInfo'; - -// Context APIs -type - TContextNotify = procedure(name: Pchar; data: Pointer; size: size_t; - data2: Pointer); stdcall; - -function clCreateContext(properties: Pcl_context_properties; - num_devices: cl_uint; devices: Pcl_device_id; notify: TContextNotify; - user_data: Pointer; var errcode_ret: cl_int): cl_context; stdcall; - external OpenCLlib name 'clCreateContext'; - -function clCreateContextFromType(properties: Pcl_context_properties; - device_type: cl_device_type; notify: TContextNotify; user_data: Pointer; - var errcode_ret: cl_int): cl_context; stdcall; - external OpenCLlib name 'clCreateContextFromType'; - -function clRetainContext(context: cl_context): cl_int; stdcall; - external OpenCLlib name 'clRetainContext'; - -function clReleaseContext(context: cl_context): cl_int; stdcall; - external OpenCLlib name 'clReleaseContext'; - -function clGetContextInfo(context: cl_context; param_name: cl_context_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetContextInfo'; - -// Command Queue APIs -function clCreateCommandQueue(context: cl_context; device: cl_device_id; - properties: cl_command_queue_properties; errcode_ret: cl_int) - : cl_command_queue; stdcall; external OpenCLlib name 'clCreateCommandQueue'; - -function clRetainCommandQueue(command_queue: cl_command_queue): cl_int; stdcall; - external OpenCLlib name 'clRetainCommandQueue'; - -function clReleaseCommandQueue(command_queue: cl_command_queue): cl_int; - stdcall; external OpenCLlib name 'clReleaseCommandQueue'; - -function clGetCommandQueueInfo(command_queue: cl_command_queue; - param_name: cl_command_queue_info; value_size: size_t; value: Pointer; - var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetCommandQueueInfo'; - -function clSetCommandQueueProperty(command_queue: cl_command_queue; - properties: cl_command_queue_properties; enable: cl_bool; - var old_properties: cl_command_queue_properties): cl_int; stdcall; - external OpenCLlib name 'clSetCommandQueueProperty'; - -// Memory Object APIs -function clCreateBuffer(context: cl_context; flags: cl_mem_flags; size: size_t; - host_ptr: Pointer; var errcode_ret: cl_int): cl_mem; stdcall; - external OpenCLlib name 'clCreateBuffer'; - -function clCreateImage2D(context: cl_context; flags: cl_mem_flags; - image_format: Pcl_image_format; image_width: size_t; image_height: size_t; - image_row_pitch: size_t; host_ptr: Pointer; var errcode_ret: cl_int): cl_mem; - stdcall; external OpenCLlib name 'clCreateImage2D'; - -function clCreateImage3D(context: cl_context; flags: cl_mem_flags; - image_format: Pcl_image_format; image_width: size_t; image_height: size_t; - image_depth: size_t; image_row_pitch: size_t; image_slice_pitch: size_t; - host_ptr: Pointer; var errcode_ret: cl_int): cl_mem; stdcall; - external OpenCLlib name 'clCreateImage3D'; - -function clRetainMemObject(memobj: cl_mem): cl_int; stdcall; - external OpenCLlib name 'clRetainMemObject'; - -function clReleaseMemObject(memobj: cl_mem): cl_int; stdcall; - external OpenCLlib name 'clReleaseMemObject'; - -function clGetSupportedImageFormats(context: cl_context; flags: cl_mem_flags; - image_type: cl_mem_object_type; num_entries: cl_uint; - image_formats: Pcl_image_format; var num_formats: cl_uint): cl_int; stdcall; - external OpenCLlib name 'clGetSupportedImageFormats'; - -function clGetMemObjectInfo(memobj: cl_mem; param_name: cl_mem_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetMemObjectInfo'; - -function clGetImageInfo(image: cl_mem; param_name: cl_image_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetImageInfo'; - -// Sampler APIs -function clCreateSampler(context: cl_context; is_norm_coords: cl_bool; - addr_mode: cl_addressing_mode; filter_mode: cl_filter_mode; - var errcode_ret: cl_int): cl_sampler; stdcall; - external OpenCLlib name 'clCreateSampler'; - -function clRetainSampler(sampler: cl_sampler): cl_int; stdcall; - external OpenCLlib name 'clRetainSampler'; - -function clReleaseSampler(sampler: cl_sampler): cl_int; stdcall; - external OpenCLlib name 'clReleaseSampler'; - -function clGetSamplerInfo(sampler: cl_sampler; param_name: cl_sampler_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetSamplerInfo'; - -// Program Object APIs -function clCreateProgramWithSource(context: cl_context; count: cl_uint; - strings: PPChar; lengths: PSIZE_T; var errcode_ret: cl_int): cl_program; - stdcall; external OpenCLlib name 'clCreateProgramWithSource'; - -type - PPByte = ^PByte; - -function clCreateProgramWithBinary(context: cl_context; num_devices: cl_uint; - device_list: Pcl_device_id; lengths: PSIZE_T; binaries: PPByte; - var binary_status: cl_int; var errcode_ret: cl_int): cl_program; stdcall; - external OpenCLlib name 'clCreateProgramWithBinary'; - -function clRetainProgram(_program: cl_program): cl_int; stdcall; - external OpenCLlib name 'clRetainProgram'; - -function clReleaseProgram(_program: cl_program): cl_int; stdcall; - external OpenCLlib name 'clReleaseProgram'; - -type - TProgramNotify = procedure(_program: cl_program; user_data: Pointer); stdcall; - - // extern cl_int - -function clBuildProgram(_program: cl_program; num_devices: cl_uint; - device_list: Pcl_device_id; options: Pchar; notify: TProgramNotify; - user_data: Pointer): cl_int; stdcall; - external OpenCLlib name 'clBuildProgram'; - -function clUnloadCompiler: cl_int; stdcall; - external OpenCLlib name 'clUnloadCompiler'; - -function clGetProgramInfo(_program: cl_program; param_name: cl_program_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetProgramInfo'; - -function clGetProgramBuildInfo(_program: cl_program; device: cl_device_id; - param_name: cl_program_build_info; value_size: size_t; value: Pointer; - var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetProgramBuildInfo'; - -// Kernel Object APIs -function clCreateKernel(_program: cl_program; kernel_name: Pchar; - var errcode_ret: cl_int): cl_kernel; stdcall; - external OpenCLlib name 'clCreateKernel'; - -function clCreateKernelsInProgram(_program: cl_program; num_kernels: cl_uint; - kernels: Pcl_kernel; var num_ret: cl_uint): cl_int; stdcall; - external OpenCLlib name 'clCreateKernelsInProgram'; - -function clRetainKernel(kernel: cl_kernel): cl_int; stdcall; - external OpenCLlib name 'clRetainKernel'; - -function clReleaseKernel(kernel: cl_kernel): cl_int; stdcall; - external OpenCLlib name 'clReleaseKernel'; - -function clSetKernelArg(kernel: cl_kernel; arg_index: cl_uint; arg_size: size_t; - arg_value: Pointer): cl_int; stdcall; - external OpenCLlib name 'clSetKernelArg'; - -function clGetKernelInfo(kernel: cl_kernel; param_name: cl_kernel_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetKernelInfo'; - -function clGetKernelWorkGroupInfo(kernel: cl_kernel; device: cl_device_id; - param_name: cl_kernel_work_group_info; value_size: size_t; value: Pointer; - size_ret: PSIZE_T): cl_int; stdcall; - external OpenCLlib name 'clGetKernelWorkGroupInfo'; - -// Event Object APIs -function clWaitForEvents(num_events: cl_uint; event_list: cl_event): cl_int; - stdcall; external OpenCLlib name 'clWaitForEvents'; - -function clGetEventInfo(event: cl_event; param_name: cl_event_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetEventInfo'; - -function clRetainEvent(event: cl_event): cl_int; stdcall; - external OpenCLlib name 'clRetainEvent'; - -function clReleaseEvent(event: cl_event): cl_int; stdcall; - external OpenCLlib name 'clReleaseEvent'; - -// Profiling APIs -function clGetEventProfilingInfo(event: cl_event; param_name: cl_profiling_info; - value_size: size_t; value: Pointer; var size_ret: size_t): cl_int; stdcall; - external OpenCLlib name 'clGetEventProfilingInfo'; - -// Flush and Finish APIs -function clFlush(command_queue: cl_command_queue): cl_int; stdcall; - external OpenCLlib name 'clFlush'; - -function clFinish(command_queue: cl_command_queue): cl_int; stdcall; - external OpenCLlib name 'clFinish'; - -// Enqueued Commands APIs -function clEnqueueReadBuffer(command_queue: cl_command_queue; buffer: cl_mem; - blocking_read: cl_bool; offset: size_t; cb: size_t; ptr: Pointer; - num_events: cl_uint; events_list: Pcl_event; event: Pcl_event): cl_int; - stdcall; external OpenCLlib name 'clEnqueueReadBuffer'; - -function clEnqueueWriteBuffer(command_queue: cl_command_queue; buffer: cl_mem; - blocking_write: cl_bool; offset: size_t; cb: size_t; ptr: Pointer; - num_events: cl_uint; events_list: Pcl_event; event: Pcl_event): cl_int; - stdcall; external OpenCLlib name 'clEnqueueWriteBuffer'; - -function clEnqueueCopyBuffer(command_queue: cl_command_queue; - src_buffer: cl_mem; dst_buffer: cl_mem; src_offset: size_t; - dst_offset: size_t; cb: size_t; num_events: cl_uint; events_list: Pcl_event; - event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueCopyBuffer'; - -function clEnqueueReadImage(command_queue: cl_command_queue; image: cl_mem; - blocking_read: cl_bool; origin: PSIZE_T; region: PSIZE_T; row_pitch: size_t; - slice_pitch: size_t; ptr: Pointer; num_events: cl_uint; - events_list: Pcl_event; event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueReadImage'; - -function clEnqueueWriteImage(command_queue: cl_command_queue; image: cl_mem; - blocking_write: cl_bool; origin: PSIZE_T; region: PSIZE_T; row_pitch: size_t; - slice_pitch: size_t; ptr: Pointer; num_events: cl_uint; - events_list: Pcl_event; event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueWriteImage'; - -function clEnqueueCopyImage(command_queue: cl_command_queue; src_image: cl_mem; - dst_image: cl_mem; src_origin: PSIZE_T; dst_origin: PSIZE_T; region: PSIZE_T; - num_events: cl_uint; events_list: Pcl_event; event: Pcl_event): cl_int; - stdcall; external OpenCLlib name 'clEnqueueCopyImage'; - -function clEnqueueCopyImageToBuffer(command_queue: cl_command_queue; - src_image: cl_mem; dst_buffre: cl_mem; src_origin: PSIZE_T; region: PSIZE_T; - dst_offset: size_t; num_events: cl_uint; events_list: Pcl_event; - event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueCopyImageToBuffer'; - -function clEnqueueCopyBufferToImage(command_queue: cl_command_queue; - src_buffer: cl_mem; dst_image: cl_mem; src_offset: size_t; - dst_origin: PSIZE_T; region: PSIZE_T; num_events: cl_uint; - events_list: Pcl_event; event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueCopyBufferToImage'; - -function clEnqueueMapBuffer(command_queue: cl_command_queue; buffer: cl_mem; - blocking_map: cl_bool; map_flags: cl_map_flags; offset: size_t; cb: size_t; - num_events: cl_uint; events_list: Pcl_event; event: Pcl_event; - var errcode_ret: cl_int): Pointer; stdcall; - external OpenCLlib name 'clEnqueueMapBuffer'; - -function clEnqueueMapImage(command_queue: cl_command_queue; image: cl_mem; - blocking_map: cl_bool; map_flags: cl_map_flags; origin: PSIZE_T; - region: PSIZE_T; row_pitch: size_t; slice_pitch: size_t; num_events: cl_uint; - events_list: Pcl_event; event: Pcl_event; var errcode_ret: cl_int): Pointer; - stdcall; external OpenCLlib name 'clEnqueueMapImage'; - -function clEnqueueUnmapMemObject(command_queue: cl_command_queue; - memobj: cl_mem; mapped_ptr: Pointer; num_events: cl_uint; - events_list: Pcl_event; event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueUnmapMemObject'; - -function clEnqueueNDRangeKernel(command_queue: cl_command_queue; - kernel: cl_kernel; work_dim: cl_uint; global_offset, global_size, - local_size: PSIZE_T; num_events: cl_uint; events_list: Pcl_event; - event: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueNDRangeKernel'; - -function clEnqueueTask(command_queue: cl_command_queue; kernel: cl_kernel; - num_events: cl_uint; events_list: Pcl_event; event: Pcl_event): cl_int; - stdcall; external OpenCLlib name 'clEnqueueTask'; - -type - TEnqueueUserProc = procedure(userdata: Pointer); stdcall; - -function clEnqueueNativeKernel(command_queue: cl_command_queue; - user_func: TEnqueueUserProc; args: Pointer; cb_args: size_t; - num_mem_objects: cl_uint; mem_list: Pcl_mem; args_mem_loc: PPointer; - num_events: cl_uint; event_wait_list: Pcl_event; event: Pcl_event): cl_int; - stdcall; external OpenCLlib name 'clEnqueueNativeKernel'; - -function clEnqueueMarker(command_queue: cl_command_queue; event: Pcl_event) - : cl_int; stdcall; external OpenCLlib name 'clEnqueueMarker'; - -function clEnqueueWaitForEvents(command_queue: cl_command_queue; - num_events: cl_uint; event_list: Pcl_event): cl_int; stdcall; - external OpenCLlib name 'clEnqueueWaitForEvents'; - -function clEnqueueBarrier(command_queue: cl_command_queue): cl_int; stdcall; - external OpenCLlib name 'clEnqueueBarrier'; - -function clErrorText(err: cl_int): string; - -implementation - -function clErrorText(err: cl_int): string; -begin - case err of - CL_DEVICE_NOT_FOUND: - clErrorText := 'CL_DEVICE_NOT_FOUND'; - CL_DEVICE_NOT_AVAILABLE: - clErrorText := 'CL_DEVICE_NOT_AVAILABLE'; - CL_DEVICE_COMPILER_NOT_AVAILABLE: - clErrorText := 'CL_DEVICE_COMPILER_NOT_AVAILABLE'; - CL_MEM_OBJECT_ALLOCATION_FAILURE: - clErrorText := 'CL_MEM_OBJECT_ALLOCATION_FAILURE'; - CL_OUT_OF_RESOURCES: - clErrorText := 'CL_OUT_OF_RESOURCES'; - CL_OUT_OF_HOST_MEMORY: - clErrorText := 'CL_OUT_OF_HOST_MEMORY'; - CL_PROFILING_INFO_NOT_AVAILABLE: - clErrorText := 'CL_PROFILING_INFO_NOT_AVAILABLE'; - CL_MEM_COPY_OVERLAP: - clErrorText := 'CL_MEM_COPY_OVERLAP'; - CL_IMAGE_FORMAT_MISMATCH: - clErrorText := 'CL_IMAGE_FORMAT_MISMATCH'; - CL_IMAGE_FORMAT_NOT_SUPPORTED: - clErrorText := 'CL_IMAGE_FORMAT_NOT_SUPPORTED'; - CL_BUILD_PROGRAM_FAILURE: - clErrorText := 'CL_BUILD_PROGRAM_FAILURE'; - CL_MAP_FAILURE: - clErrorText := 'CL_MAP_FAILURE'; - - CL_INVALID_VALUE: - clErrorText := 'CL_INVALID_VALUE'; - CL_INVALID_DEVICE_TYPE: - clErrorText := 'CL_INVALID_DEVICE_TYPE'; - CL_INVALID_PLATFORM: - clErrorText := 'CL_INVALID_PLATFORM'; - CL_INVALID_DEVICE: - clErrorText := 'CL_INVALID_DEVICE'; - CL_INVALID_CONTEXT: - clErrorText := 'CL_INVALID_CONTEXT'; - CL_INVALID_QUEUE_PROPERTIES: - clErrorText := 'CL_INVALID_QUEUE_PROPERTIES'; - CL_INVALID_COMMAND_QUEUE: - clErrorText := 'CL_INVALID_COMMAND_QUEUE'; - CL_INVALID_HOST_PTR: - clErrorText := 'CL_INVALID_HOST_PTR'; - CL_INVALID_MEM_OBJECT: - clErrorText := 'CL_INVALID_MEM_OBJECT'; - CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: - clErrorText := 'CL_INVALID_IMAGE_FORMAT_DESCRIPTOR'; - CL_INVALID_IMAGE_SIZE: - clErrorText := 'CL_INVALID_IMAGE_SIZE'; - CL_INVALID_SAMPLER: - clErrorText := 'CL_INVALID_SAMPLER'; - CL_INVALID_BINARY: - clErrorText := 'CL_INVALID_BINARY'; - CL_INVALID_BUILD_OPTIONS: - clErrorText := 'CL_INVALID_BUILD_OPTIONS'; - CL_INVALID_PROGRAM: - clErrorText := 'CL_INVALID_PROGRAM'; - CL_INVALID_PROGRAM_EXECUTABLE: - clErrorText := 'CL_INVALID_PROGRAM_EXECUTABLE'; - CL_INVALID_KERNEL_NAME: - clErrorText := 'CL_INVALID_KERNEL_NAME'; - CL_INVALID_KERNEL_DEFINITION: - clErrorText := 'CL_INVALID_KERNEL_DEFINITION'; - CL_INVALID_KERNEL: - clErrorText := 'CL_INVALID_KERNEL'; - CL_INVALID_ARG_INDEX: - clErrorText := 'CL_INVALID_ARG_INDEX'; - CL_INVALID_ARG_VALUE: - clErrorText := 'CL_INVALID_ARG_VALUE'; - CL_INVALID_ARG_SIZE: - clErrorText := 'CL_INVALID_ARG_SIZE'; - CL_INVALID_KERNEL_ARGS: - clErrorText := 'CL_INVALID_KERNEL_ARGS'; - CL_INVALID_WORK_DIMENSION: - clErrorText := 'CL_INVALID_WORK_DIMENSION'; - CL_INVALID_WORK_GROUP_SIZE: - clErrorText := 'CL_INVALID_WORK_GROUP_SIZE'; - CL_INVALID_WORK_ITEM_SIZE: - clErrorText := 'CL_INVALID_WORK_ITEM_SIZE'; - CL_INVALID_GLOBAL_OFFSET: - clErrorText := 'CL_INVALID_GLOBAL_OFFSET'; - CL_INVALID_EVENT_WAIT_LIST: - clErrorText := 'CL_INVALID_EVENT_WAIT_LIST'; - CL_INVALID_EVENT: - clErrorText := 'CL_INVALID_EVENT'; - CL_INVALID_OPERATION: - clErrorText := 'CL_INVALID_OPERATION'; - CL_INVALID_GL_OBJECT: - clErrorText := 'CL_INVALID_GL_OBJECT'; - CL_INVALID_BUFFER_SIZE: - clErrorText := 'CL_INVALID_BUFFER_SIZE'; - CL_INVALID_MIP_LEVEL: - clErrorText := 'CL_INVALID_MIP_LEVEL'; - else - clErrorText := 'Unknown OpenCL error'; - end; -end; - -end. diff --git a/dbgenerator/DbgMain.pas b/dbgenerator/DbgMain.pas index 9d3ac86..3ccc2c8 100644 --- a/dbgenerator/DbgMain.pas +++ b/dbgenerator/DbgMain.pas @@ -16,7 +16,7 @@ type TEncodeOptions = record ChunkSize, Threads: Integer; Method: String; - Format: String; + BlockSize: Integer; end; procedure PrintHelp; @@ -104,6 +104,14 @@ begin S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); + S := ArgParse.AsString('-c', 0, '1792mb'); + S := ReplaceText(S, 'KB', '* 1024^1'); + S := ReplaceText(S, 'MB', '* 1024^2'); + S := ReplaceText(S, 'GB', '* 1024^3'); + S := ReplaceText(S, 'K', '* 1024^1'); + S := ReplaceText(S, 'M', '* 1024^2'); + S := ReplaceText(S, 'G', '* 1024^3'); + Options.BlockSize := Round(ExpParse.Evaluate(S)); finally ArgParse.Free; ExpParse.Free; @@ -161,7 +169,7 @@ var CountPos: NativeInt; BaseDir: String; LList: TArray; - LSInfo: PScanInfo; + LSInfo: TScanInfo; LEntry: TEntryStruct; LBytes: TBytes; Hash: Cardinal; @@ -171,6 +179,7 @@ var OStream, MStream: TMemoryStream; DataStore: TDataStore1; Tasks: TArray; + NStream: TArray; InfoStore: TArray>; begin SetLength(SearchInfo, $10000); @@ -189,6 +198,13 @@ begin else BaseDir := ExtractFilePath(TPath.GetFullPath(Input1)); LList := GetFileList([Input1], True); + SetLength(Tasks, Options.Threads); + SetLength(Tasks, Options.Threads); + for I := Low(Tasks) to High(Tasks) do + begin + Tasks[I] := TTask.Create(I); + NStream[I] := TMemoryStream.Create; + end; for I := Low(LList) to High(LList) do begin if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then @@ -201,23 +217,22 @@ begin WordRec(A).Bytes[1] := Buffer[1]; B := Buffer[MinSize1 - 1]; J := MinSize1; - New(LSInfo); - LSInfo^.CRCSize := J; - LSInfo^.ActualSize := FileSize(LList[I]); - LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J); - LSInfo^.CRC2 := LSInfo^.CRC1; - while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do + LSInfo.CRCSize := J; + LSInfo.ActualSize := FileSize(LList[I]); + LSInfo.CRC1 := Utils.Hash32(0, @Buffer[0], J); + LSInfo.CRC2 := LSInfo.CRC1; + while (J > 0) and (LSInfo.CRCSize < Options.ChunkSize) do begin - J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize, + J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo.CRCSize, BufferSize)); - Inc(LSInfo^.CRCSize, J); - LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J); + Inc(LSInfo.CRCSize, J); + LSInfo.CRC2 := Utils.Hash32(LSInfo.CRC2, @Buffer[0], J); end; + Insert(LSInfo, SearchInfo[A, B], Length(SearchInfo[A, B])); + Inc(SearchCount[A, B]); finally Free; end; - Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B])); - Inc(SearchCount[A, B]); end else if FileSize(LList[I]) < MinSize1 then WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)', @@ -226,9 +241,10 @@ begin WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)', [ReplaceText(LList[I], BaseDir, ''), Integer.MaxValue])); end; + for I := Low(Tasks) to High(Tasks) do + NStream[I].Free; DataStore := TDataStore1.Create(nil, True, Options.Threads, Options.ChunkSize); - SetLength(Tasks, Options.Threads); SetLength(InfoStore, Options.Threads); OStream := TMemoryStream.Create; MStream := TMemoryStream.Create; @@ -242,10 +258,9 @@ begin OStream.Position := OStream.Size; Found1 := False; try - for I := Low(Tasks) to High(Tasks) do + for I := Low(InfoStore) to High(InfoStore) do begin InfoStore[I] := TListEx.Create(EntryStructCmp); - Tasks[I] := TTask.Create(I); Tasks[I].Perform( procedure(X: IntPtr) var @@ -378,15 +393,18 @@ begin if Found1 then OStream.SaveToFile(Output); finally - for I := Low(Tasks) to High(Tasks) do + for I := Low(InfoStore) to High(InfoStore) do begin InfoStore[I].Free; Tasks[I].Free; + NStream[I].Free; end; DataStore.Free; OStream.Free; MStream.Free; end; + for I := Low(InfoStore) to High(InfoStore) do + Tasks[I].Free; end; end. diff --git a/dbgenerator/DbgUtils.pas b/dbgenerator/DbgUtils.pas index 44f1774..03e60d6 100644 --- a/dbgenerator/DbgUtils.pas +++ b/dbgenerator/DbgUtils.pas @@ -10,6 +10,11 @@ resourcestring SPrecompSep1 = '+'; SPrecompSep2 = ':'; SPrecompSep3 = ','; + SPrecompSep4 = '/'; + SPrecompSep5 = '/'; + XTOOL_MAPSUF1 = '-tmp'; + XTOOL_MAPSUF2 = '_mapped.io'; + XTOOL_MAPSUF3 = '.tmp'; const XTOOL_DB = $31445458; diff --git a/imports/BrunsliDLL.pas b/imports/BrunsliDLL.pas index 13bb944..3bcda33 100644 --- a/imports/BrunsliDLL.pas +++ b/imports/BrunsliDLL.pas @@ -3,6 +3,7 @@ unit BrunsliDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -39,41 +40,31 @@ var implementation var - DLLHandle: THandle; - S: String; + Lib: TLibImport; procedure Init; begin - S := 'brunsli.dll'; - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + S)); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'brunsli.dll'); + if Lib.Loaded then begin - @brunsli_alloc_JPEGData := GetProcAddress(DLLHandle, - 'brunsli_alloc_JPEGData'); - @brunsli_free_JPEGData := GetProcAddress(DLLHandle, - 'brunsli_free_JPEGData'); - @brunsli_GetMaximumEncodedSize := GetProcAddress(DLLHandle, - 'brunsli_GetMaximumEncodedSize'); - @brunsli_ReadJpeg := GetProcAddress(DLLHandle, 'brunsli_ReadJpeg'); - @brunsli_EncodeJpeg := GetProcAddress(DLLHandle, 'brunsli_EncodeJpeg'); - @brunsli_DecodeJpeg := GetProcAddress(DLLHandle, 'brunsli_DecodeJpeg'); - @brunsli_alloc_JPEGOutput := GetProcAddress(DLLHandle, - 'brunsli_alloc_JPEGOutput'); - @brunsli_free_JPEGOutput := GetProcAddress(DLLHandle, - 'brunsli_free_JPEGOutput'); - @brunsli_WriteJpeg := GetProcAddress(DLLHandle, 'brunsli_WriteJpeg'); + @brunsli_alloc_JPEGData := Lib.GetProcAddr('brunsli_alloc_JPEGData'); + @brunsli_free_JPEGData := Lib.GetProcAddr('brunsli_free_JPEGData'); + @brunsli_GetMaximumEncodedSize := + Lib.GetProcAddr('brunsli_GetMaximumEncodedSize'); + @brunsli_ReadJpeg := Lib.GetProcAddr('brunsli_ReadJpeg'); + @brunsli_EncodeJpeg := Lib.GetProcAddr('brunsli_EncodeJpeg'); + @brunsli_DecodeJpeg := Lib.GetProcAddr('brunsli_DecodeJpeg'); + @brunsli_alloc_JPEGOutput := Lib.GetProcAddr('brunsli_alloc_JPEGOutput'); + @brunsli_free_JPEGOutput := Lib.GetProcAddr('brunsli_free_JPEGOutput'); + @brunsli_WriteJpeg := Lib.GetProcAddr('brunsli_WriteJpeg'); DLLLoaded := Assigned(brunsli_alloc_JPEGData) and Assigned(brunsli_alloc_JPEGOutput); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - FreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/FLACDLL.pas b/imports/FLACDLL.pas index 4c14445..60e44b6 100644 --- a/imports/FLACDLL.pas +++ b/imports/FLACDLL.pas @@ -3,6 +3,7 @@ unit FLACDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -121,68 +122,60 @@ var implementation var - DLLHandle: THandle; + Lib: TLibImport; procedure Init; begin - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + - 'libFLAC_dynamic.dll')); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + + 'libFLAC_dynamic.dll'); + if Lib.Loaded then begin - @FLAC__stream_encoder_new := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_new'); - @FLAC__stream_encoder_set_verify := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_set_verify'); - @FLAC__stream_encoder_set_channels := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_set_channels'); + @FLAC__stream_encoder_new := Lib.GetProcAddr('FLAC__stream_encoder_new'); + @FLAC__stream_encoder_set_verify := + Lib.GetProcAddr('FLAC__stream_encoder_set_verify'); + @FLAC__stream_encoder_set_channels := + Lib.GetProcAddr('FLAC__stream_encoder_set_channels'); @FLAC__stream_encoder_set_compression_level := - GetProcAddress(DLLHandle, 'FLAC__stream_encoder_set_compression_level'); + Lib.GetProcAddr('FLAC__stream_encoder_set_compression_level'); @FLAC__stream_encoder_set_bits_per_sample := - GetProcAddress(DLLHandle, 'FLAC__stream_encoder_set_bits_per_sample'); + Lib.GetProcAddr('FLAC__stream_encoder_set_bits_per_sample'); @FLAC__stream_encoder_set_sample_rate := - GetProcAddress(DLLHandle, 'FLAC__stream_encoder_set_sample_rate'); + Lib.GetProcAddr('FLAC__stream_encoder_set_sample_rate'); @FLAC__stream_encoder_set_total_samples_estimate := - GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_set_total_samples_estimate'); - @FLAC__stream_encoder_init_stream := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_init_stream'); - @FLAC__stream_encoder_init_file := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_init_file'); + Lib.GetProcAddr('FLAC__stream_encoder_set_total_samples_estimate'); + @FLAC__stream_encoder_init_stream := + Lib.GetProcAddr('FLAC__stream_encoder_init_stream'); + @FLAC__stream_encoder_init_file := + Lib.GetProcAddr('FLAC__stream_encoder_init_file'); @FLAC__stream_encoder_process_interleaved := - GetProcAddress(DLLHandle, 'FLAC__stream_encoder_process_interleaved'); - @FLAC__stream_encoder_finish := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_finish'); - @FLAC__stream_encoder_delete := GetProcAddress(DLLHandle, - 'FLAC__stream_encoder_delete'); - @FLAC__stream_decoder_new := GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_new'); - @FLAC__stream_decoder_init_stream := GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_init_stream'); - @FLAC__stream_decoder_init_file := GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_init_file'); - @FLAC__stream_decoder_get_channels := GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_get_channels'); + Lib.GetProcAddr('FLAC__stream_encoder_process_interleaved'); + @FLAC__stream_encoder_finish := + Lib.GetProcAddr('FLAC__stream_encoder_finish'); + @FLAC__stream_encoder_delete := + Lib.GetProcAddr('FLAC__stream_encoder_delete'); + @FLAC__stream_decoder_new := Lib.GetProcAddr('FLAC__stream_decoder_new'); + @FLAC__stream_decoder_init_stream := + Lib.GetProcAddr('FLAC__stream_decoder_init_stream'); + @FLAC__stream_decoder_init_file := + Lib.GetProcAddr('FLAC__stream_decoder_init_file'); + @FLAC__stream_decoder_get_channels := + Lib.GetProcAddr('FLAC__stream_decoder_get_channels'); @FLAC__stream_decoder_get_bits_per_sample := - GetProcAddress(DLLHandle, 'FLAC__stream_decoder_get_bits_per_sample'); + Lib.GetProcAddr('FLAC__stream_decoder_get_bits_per_sample'); @FLAC__stream_decoder_process_until_end_of_stream := - GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_process_until_end_of_stream'); - @FLAC__stream_decoder_finish := GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_finish'); - @FLAC__stream_decoder_delete := GetProcAddress(DLLHandle, - 'FLAC__stream_decoder_delete'); + Lib.GetProcAddr('FLAC__stream_decoder_process_until_end_of_stream'); + @FLAC__stream_decoder_finish := + Lib.GetProcAddr('FLAC__stream_decoder_finish'); + @FLAC__stream_decoder_delete := + Lib.GetProcAddr('FLAC__stream_decoder_delete'); DLLLoaded := Assigned(FLAC__stream_encoder_new) and Assigned(FLAC__stream_decoder_new); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - FreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/FLZMA2DLL.pas b/imports/FLZMA2DLL.pas index 23ba52b..e89a7ca 100644 --- a/imports/FLZMA2DLL.pas +++ b/imports/FLZMA2DLL.pas @@ -3,7 +3,7 @@ unit FLZMA2DLL; interface uses - MemoryModule, + LibImport, WinAPI.Windows, System.SysUtils, System.Classes, System.Types; @@ -24,6 +24,82 @@ type pos: size_t; end; +type + FL2_cParameter = ( + (* compression parameters *) + FL2_p_compressionLevel, + (* Update all compression parameters according to pre-defined cLevel table + * Default level is FL2_CLEVEL_DEFAULT==6. + * Setting FL2_p_highCompression to 1 switches to an alternate cLevel table. *) + FL2_p_highCompression, + (* Maximize compression ratio for a given dictionary size. + * Levels 1..10 = dictionaryLog 20..29 (1 Mb..512 Mb). + * Typically provides a poor speed/ratio tradeoff. *) + FL2_p_dictionaryLog, + (* Maximum allowed back-reference distance, expressed as power of 2. + * Must be clamped between FL2_DICTLOG_MIN and FL2_DICTLOG_MAX. + * Default = 24 *) + FL2_p_dictionarySize, (* Same as above but expressed as an absolute value. + * Must be clamped between FL2_DICTSIZE_MIN and FL2_DICTSIZE_MAX. + * Default = 16 Mb *) + FL2_p_overlapFraction, + (* The radix match finder is block-based, so some overlap is retained from + * each block to improve compression of the next. This value is expressed + * as n / 16 of the block size (dictionary size). Larger values are slower. + * Values above 2 mostly yield only a small improvement in compression. + * A large value for a small dictionary may worsen multithreaded compression. + * Default = 2 *) + FL2_p_resetInterval, + (* For multithreaded decompression. A dictionary reset will occur + * after each dictionarySize * resetInterval bytes of input. + * Default = 4 *) + FL2_p_bufferResize, + (* Buffering speeds up the matchfinder. Buffer resize determines the percentage of + * the normal buffer size used, which depends on dictionary size. + * 0=50, 1=75, 2=100, 3=150, 4=200. Higher number = slower, better + * compression, higher memory usage. A CPU with a large memory cache + * may make effective use of a larger buffer. + * Default = 2 *) + FL2_p_hybridChainLog, + (* Size of the hybrid mode HC3 hash chain, as a power of 2. + * Resulting table size is (1 << (chainLog+2)) bytes. + * Larger tables result in better and slower compression. + * This parameter is only used by the hybrid "ultra" strategy. + * Default = 9 *) + FL2_p_hybridCycles, + (* Number of search attempts made by the HC3 match finder. + * Used only by the hybrid "ultra" strategy. + * More attempts result in slightly better and slower compression. + * Default = 1 *) + FL2_p_searchDepth, + (* Match finder will resolve string matches up to this length. If a longer + * match exists further back in the input, it will not be found. + * Default = 42 *) + FL2_p_fastLength, (* Only useful for strategies >= opt. + * Length of match considered "good enough" to stop search. + * Larger values make compression stronger and slower. + * Default = 48 *) + FL2_p_divideAndConquer, + (* Split long chains of 2-byte matches into shorter chains with a small overlap + * for further processing. Allows buffering of all chains at length 2. + * Faster, less compression. Generally a good tradeoff. + * Default = enabled *) + FL2_p_strategy, (* 1 = fast; 2 = optimized, 3 = ultra (hybrid mode). + * The higher the value of the selected strategy, the more complex it is, + * resulting in stronger and slower compression. + * Default = ultra *) + FL2_p_literalCtxBits, (* lc value for LZMA2 encoder + * Default = 3 *) + FL2_p_literalPosBits, (* lp value for LZMA2 encoder + * Default = 0 *) + FL2_p_posBits, (* pb value for LZMA2 encoder + * Default = 2 *) + FL2_p_omitProperties, + (* Omit the property byte at the start of the stream. For use within 7-zip *) + (* or other containers which store the property byte elsewhere. *) + (* A stream compressed under this setting cannot be decoded by this library. *) + FL2_cParameter_Force32 = $40000000); + var FL2_compress: function(dst: Pointer; dstCapacity: size_t; const src: Pointer; srcSize: size_t; compressionLevel: Integer): size_t cdecl; @@ -63,13 +139,20 @@ var input: PFL2_inBuffer): size_t cdecl; FL2_endStream: function(fcs: Pointer; output: PFL2_outBuffer): size_t cdecl; + FL2_isError: function(code: size_t): Cardinal cdecl; + FL2_CStream_setParameter: function(fcs: Pointer; param: FL2_cParameter; + value: size_t): size_t cdecl; + FL2_CStream_getParameter: function(fcs: Pointer; param: FL2_cParameter) + : size_t cdecl; + FL2_setDStreamMemoryLimitMt: procedure(fds: Pointer; limit: size_t)cdecl; DLLLoaded: boolean = False; type TLZMACRec = record Threads: Integer; Level: Integer; + HighCompress: boolean; procedure Parse(S: String); end; @@ -86,8 +169,9 @@ type FProp: TLZMACRec; FOutput: TStream; FBuffer: array [0 .. FBufferSize - 1] of Byte; + FInitialized: boolean; public - constructor Create(AOutput: TStream; AConfig: String = 't50p'); + constructor Create(AOutput: TStream; AConfig: String); destructor Destroy; override; function Write(const Buffer; Count: LongInt): LongInt; override; end; @@ -113,8 +197,7 @@ uses Utils; var - DLLStream: TResourceStream; - DLLHandle: TMemoryModule; + Lib: TLibImport; procedure TLZMACRec.Parse(S: string); var @@ -122,7 +205,8 @@ var I, J: Integer; begin Threads := 1; - Level := 5; + Level := 6; + HighCompress := False; List := DecodeStr(S, ':'); for I := Low(List) to High(List) do begin @@ -130,6 +214,8 @@ begin Threads := ConvertToThreads(List[I].Substring(1)); if List[I].StartsWith('l', True) then Level := List[I].Substring(1).ToInteger; + if List[I].StartsWith('hi', True) then + HighCompress := List[I].Substring(2).ToBoolean; end; end; @@ -148,15 +234,22 @@ begin end; constructor TLZMACompressStream.Create(AOutput: TStream; AConfig: String); +var + LConfig: String; begin inherited Create; - FProp.Parse(AConfig); + LConfig := AConfig; + if LConfig = '' then + LConfig := 't50p'; + FProp.Parse(LConfig); FOutput := AOutput; if FProp.Threads > 1 then FCtx := FL2_createCStreamMt(FProp.Threads, 0) else FCtx := FL2_createCStream; - FL2_initCStream(FCtx, FProp.Level); + FL2_CStream_setParameter(FCtx, FL2_cParameter.FL2_p_highCompression, + Integer(FProp.HighCompress)); + FInitialized := False; end; destructor TLZMACompressStream.Destroy; @@ -164,14 +257,17 @@ var Oup: FL2_outBuffer; Res: size_t; begin - Oup.dst := @FBuffer[0]; - Oup.size := FBufferSize; - Oup.pos := 0; - repeat - Res := FL2_endStream(FCtx, @Oup); - FOutput.WriteBuffer(FBuffer[0], Oup.pos); + if FInitialized then + begin + Oup.dst := @FBuffer[0]; + Oup.size := FBufferSize; Oup.pos := 0; - until Res = 0; + repeat + Res := FL2_endStream(FCtx, @Oup); + FOutput.WriteBuffer(FBuffer[0], Oup.pos); + Oup.pos := 0; + until Res = 0; + end; FL2_freeCCtx(FCtx); inherited Destroy; end; @@ -182,6 +278,11 @@ var Oup: FL2_outBuffer; begin Result := 0; + if not FInitialized then + begin + FL2_initCStream(FCtx, FProp.Level); + FInitialized := True; + end; Inp.src := PByte(@Buffer); Inp.size := Count; Inp.pos := 0; @@ -200,12 +301,23 @@ begin end; constructor TLZMADecompressStream.Create(AInput: TStream; AConfig: String); +var + LConfig: String; + LSize: Int64; begin inherited Create; - FProp.Parse(AConfig); + LConfig := AConfig; + if LConfig = '' then + LConfig := 't50p'; + FProp.Parse(LConfig); FInput := AInput; + LSize := 0; + LSize := LSize.MaxValue; if FProp.Threads > 1 then - FCtx := FL2_createDStreamMt(FProp.Threads) + begin + FCtx := FL2_createDStreamMt(FProp.Threads); + FL2_setDStreamMemoryLimitMt(FCtx, LSize); + end else FCtx := FL2_createDStream; FL2_initDStream(FCtx); @@ -255,50 +367,44 @@ end; procedure Init; begin - DLLStream := TResourceStream.Create(HInstance, 'fast_lzma2', RT_RCDATA); - DLLHandle := MemoryLoadLibary(DLLStream.Memory); - if Assigned(DLLHandle) then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'fast-lzma2.dll'); + if Lib.Loaded then begin - @FL2_compress := MemoryGetProcAddress(DLLHandle, 'FL2_compress'); - @FL2_compressMt := MemoryGetProcAddress(DLLHandle, 'FL2_compressMt'); - @FL2_decompress := MemoryGetProcAddress(DLLHandle, 'FL2_decompress'); - @FL2_decompressMt := MemoryGetProcAddress(DLLHandle, 'FL2_decompressMt'); - @FL2_createCCtx := MemoryGetProcAddress(DLLHandle, 'FL2_createCCtx'); - @FL2_createCCtxMt := MemoryGetProcAddress(DLLHandle, 'FL2_createCCtxMt'); - @FL2_freeCCtx := MemoryGetProcAddress(DLLHandle, 'FL2_freeCCtx'); - @FL2_compressCCtx := MemoryGetProcAddress(DLLHandle, 'FL2_compressCCtx'); - @FL2_createDCtx := MemoryGetProcAddress(DLLHandle, 'FL2_createDCtx'); - @FL2_createDCtxMt := MemoryGetProcAddress(DLLHandle, 'FL2_createDCtxMt'); - @FL2_freeDCtx := MemoryGetProcAddress(DLLHandle, 'FL2_freeDCtx'); - @FL2_decompressDCtx := MemoryGetProcAddress(DLLHandle, - 'FL2_decompressDCtx'); - @FL2_createCStream := MemoryGetProcAddress(DLLHandle, 'FL2_createCStream'); - @FL2_createCStreamMt := MemoryGetProcAddress(DLLHandle, - 'FL2_createCStreamMt'); - @FL2_freeCStream := MemoryGetProcAddress(DLLHandle, 'FL2_freeCStream'); - @FL2_initCStream := MemoryGetProcAddress(DLLHandle, 'FL2_initCStream'); - @FL2_compressStream := MemoryGetProcAddress(DLLHandle, - 'FL2_compressStream'); - @FL2_createDStream := MemoryGetProcAddress(DLLHandle, 'FL2_createDStream'); - @FL2_createDStreamMt := MemoryGetProcAddress(DLLHandle, - 'FL2_createDStreamMt'); - @FL2_freeDStream := MemoryGetProcAddress(DLLHandle, 'FL2_freeDStream'); - @FL2_initDStream := MemoryGetProcAddress(DLLHandle, 'FL2_initDStream'); - @FL2_decompressStream := MemoryGetProcAddress(DLLHandle, - 'FL2_decompressStream'); - @FL2_endStream := MemoryGetProcAddress(DLLHandle, 'FL2_endStream'); - @FL2_isError := MemoryGetProcAddress(DLLHandle, 'FL2_isError'); + @FL2_compress := Lib.GetProcAddr('FL2_compress'); + @FL2_compressMt := Lib.GetProcAddr('FL2_compressMt'); + @FL2_decompress := Lib.GetProcAddr('FL2_decompress'); + @FL2_decompressMt := Lib.GetProcAddr('FL2_decompressMt'); + @FL2_createCCtx := Lib.GetProcAddr('FL2_createCCtx'); + @FL2_createCCtxMt := Lib.GetProcAddr('FL2_createCCtxMt'); + @FL2_freeCCtx := Lib.GetProcAddr('FL2_freeCCtx'); + @FL2_compressCCtx := Lib.GetProcAddr('FL2_compressCCtx'); + @FL2_createDCtx := Lib.GetProcAddr('FL2_createDCtx'); + @FL2_createDCtxMt := Lib.GetProcAddr('FL2_createDCtxMt'); + @FL2_freeDCtx := Lib.GetProcAddr('FL2_freeDCtx'); + @FL2_decompressDCtx := Lib.GetProcAddr('FL2_decompressDCtx'); + @FL2_createCStream := Lib.GetProcAddr('FL2_createCStream'); + @FL2_createCStreamMt := Lib.GetProcAddr('FL2_createCStreamMt'); + @FL2_freeCStream := Lib.GetProcAddr('FL2_freeCStream'); + @FL2_initCStream := Lib.GetProcAddr('FL2_initCStream'); + @FL2_compressStream := Lib.GetProcAddr('FL2_compressStream'); + @FL2_createDStream := Lib.GetProcAddr('FL2_createDStream'); + @FL2_createDStreamMt := Lib.GetProcAddr('FL2_createDStreamMt'); + @FL2_freeDStream := Lib.GetProcAddr('FL2_freeDStream'); + @FL2_initDStream := Lib.GetProcAddr('FL2_initDStream'); + @FL2_decompressStream := Lib.GetProcAddr('FL2_decompressStream'); + @FL2_endStream := Lib.GetProcAddr('FL2_endStream'); + @FL2_isError := Lib.GetProcAddr('FL2_isError'); + @FL2_CStream_setParameter := Lib.GetProcAddr('FL2_CStream_setParameter'); + @FL2_CStream_getParameter := Lib.GetProcAddr('FL2_CStream_getParameter'); + @FL2_setDStreamMemoryLimitMt := + Lib.GetProcAddr('FL2_setDStreamMemoryLimitMt'); DLLLoaded := Assigned(FL2_compress) and Assigned(FL2_decompress); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - MemoryFreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/GrittibanzliDLL.pas b/imports/GrittibanzliDLL.pas new file mode 100644 index 0000000..ed0a6ec --- /dev/null +++ b/imports/GrittibanzliDLL.pas @@ -0,0 +1,52 @@ +unit GrittibanzliDLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils, System.Classes; + +var + Grittibanzli: function(const src: Pointer; srcSize: Cardinal; dst1: Pointer; + dst1Capacity: PCardinal; dst2: Pointer; dst2Capacity: PCardinal) + : boolean cdecl; + Ungrittibanzli: function(const src1: Pointer; src1Size: Cardinal; + const src2: Pointer; src2Size: Cardinal; dst: Pointer; + dstCapacity: PCardinal): boolean cdecl; + DLLLoaded: boolean = False; + +implementation + +var + DLLHandle: THandle; + +procedure Init; +begin + DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + + 'grittibanzli_dll.dll')); + if DLLHandle >= 32 then + begin + @Grittibanzli := GetProcAddress(DLLHandle, '__Grittibanzli'); + @Ungrittibanzli := GetProcAddress(DLLHandle, '__Ungrittibanzli'); + DLLLoaded := Assigned(Grittibanzli) and Assigned(Ungrittibanzli); + end + else + DLLLoaded := False; +end; + +procedure Deinit; +begin + if not DLLLoaded then + exit; + FreeLibrary(DLLHandle); +end; + +initialization + +Init; + +finalization + +Deinit; + +end. diff --git a/imports/JoJpegDLL.pas b/imports/JoJpegDLL.pas index e2c7741..94e7e54 100644 --- a/imports/JoJpegDLL.pas +++ b/imports/JoJpegDLL.pas @@ -3,6 +3,7 @@ unit JoJpegDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -32,30 +33,25 @@ var implementation var - DLLHandle: THandle; + Lib: TLibImport; procedure Init; begin - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + - 'jojpeg_dll.dll')); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'jojpeg_dll.dll'); + if Lib.Loaded then begin - @jojpeg_Init := GetProcAddress(DLLHandle, 'jojpeg_Init'); - @jojpeg_Quit := GetProcAddress(DLLHandle, 'jojpeg_Quit'); - @jojpeg_Loop := GetProcAddress(DLLHandle, 'jojpeg_Loop'); - @jojpeg_Getvalue := GetProcAddress(DLLHandle, 'jojpeg_Getvalue'); - @jojpeg_Addbuf := GetProcAddress(DLLHandle, 'jojpeg_Addbuf'); + @jojpeg_Init := Lib.GetProcAddr('jojpeg_Init'); + @jojpeg_Quit := Lib.GetProcAddr('jojpeg_Quit'); + @jojpeg_Loop := Lib.GetProcAddr('jojpeg_Loop'); + @jojpeg_Getvalue := Lib.GetProcAddr('jojpeg_Getvalue'); + @jojpeg_Addbuf := Lib.GetProcAddr('jojpeg_Addbuf'); DLLLoaded := Assigned(jojpeg_Init); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - FreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/LZ4DLL.pas b/imports/LZ4DLL.pas index 1eccf75..d874fa6 100644 --- a/imports/LZ4DLL.pas +++ b/imports/LZ4DLL.pas @@ -3,13 +3,23 @@ unit LZ4DLL; interface uses + LibImport, WinAPI.Windows, - System.SysUtils; + System.SysUtils, System.Math; const LZ4F_VERSION = 100; type + PLZ4_streamDecode_t = ^LZ4_streamDecode_t; + LZ4_streamDecode_t = array [0 .. 1 shl 9 - 1] of byte; + + PLZ4_stream_t = ^LZ4_stream_t; + LZ4_stream_t = array [0 .. 1 shl 9 - 1] of byte; + + PLZ4_streamHC_t = ^LZ4_streamHC_t; + LZ4_streamHC_t = array [0 .. 1 shl 9 - 1] of byte; + LZ4F_errorCode_t = type size_t; LZ4F_blockSizeID_t = (LZ4F_default = 0, LZ4F_max64KB = 4, LZ4F_max256KB = 5, @@ -83,67 +93,65 @@ var LZ4F_getFrameInfo: function(dctx: LZ4F_dctx; out frameInfoPtr: LZ4F_frameInfo_t; srcBuffer: Pointer; out srcSizePtr: size_t): size_t cdecl; + LZ4_createStreamDecode: function: PLZ4_streamDecode_t cdecl; + LZ4_freeStreamDecode: function(LZ4_stream: PLZ4_streamDecode_t) + : Integer cdecl; + LZ4_decompress_safe_continue: function(LZ4_stream: PLZ4_streamDecode_t; + const src: Pointer; dst: Pointer; srcSize: Integer; dstCapacity: Integer) + : Integer cdecl; + LZ4_createStream: function: PLZ4_stream_t cdecl; + LZ4_freeStream: function(streamPtr: PLZ4_stream_t): Integer cdecl; + LZ4_resetStream: procedure(streamHCPtr: PLZ4_stream_t)cdecl; + LZ4_compress_fast_continue: function(streamPtr: PLZ4_stream_t; + const src: Pointer; dst: Pointer; srcSize: Integer; maxDstSize: Integer; + acceleration: Integer): Integer cdecl; + LZ4_createStreamHC: function: PLZ4_streamHC_t cdecl; + LZ4_freeStreamHC: function(streamHCPtr: PLZ4_streamHC_t): Integer cdecl; + LZ4_resetStreamHC: procedure(streamHCPtr: PLZ4_streamHC_t; + compressionLevel: Integer)cdecl; + LZ4_compress_HC_continue: function(streamHCPtr: PLZ4_streamHC_t; + const src: Pointer; dst: Pointer; srcSize: Integer; maxDstSize: Integer) + : Integer cdecl; DLLLoaded: Boolean = False; function LZ4F_decompress_safe(source: Pointer; dest: Pointer; - compressedSize: Integer; maxDecompressedSize: Integer): Integer; + sourceSize: Integer; destSize: Integer; compressedSize: PInteger = nil; + blockSize: PInteger = nil): Integer; +function LZ4_compress_block(src, dst: Pointer; + srcSize, dstCapacity: Integer): Integer; implementation -var - DLLHandle: THandle; - -procedure Init(Filename: String); -begin - if DLLLoaded then - Exit; - DLLHandle := 0; - DLLHandle := LoadLibrary(PWideChar(ExtractFilePath(ParamStr(0)) + Filename)); - if DLLHandle >= 32 then - begin - @LZ4_decompress_safe := GetProcAddress(DLLHandle, 'LZ4_decompress_safe'); - @LZ4_decompress_fast := GetProcAddress(DLLHandle, 'LZ4_decompress_fast'); - @LZ4_compress_default := GetProcAddress(DLLHandle, 'LZ4_compress_default'); - @LZ4_compress_fast := GetProcAddress(DLLHandle, 'LZ4_compress_fast'); - @LZ4_compress_HC := GetProcAddress(DLLHandle, 'LZ4_compress_HC'); - @LZ4_compressHC2 := GetProcAddress(DLLHandle, 'LZ4_compressHC2'); - @LZ4F_compressFrame := GetProcAddress(DLLHandle, 'LZ4F_compressFrame'); - @LZ4F_compressFrameBound := GetProcAddress(DLLHandle, - 'LZ4F_compressFrameBound'); - @LZ4F_createDecompressionContext := GetProcAddress(DLLHandle, - 'LZ4F_createDecompressionContext'); - @LZ4F_freeDecompressionContext := GetProcAddress(DLLHandle, - 'LZ4F_freeDecompressionContext'); - @LZ4F_decompress := GetProcAddress(DLLHandle, 'LZ4F_decompress'); - @LZ4F_getFrameInfo := GetProcAddress(DLLHandle, 'LZ4F_getFrameInfo'); - DLLLoaded := Assigned(LZ4_decompress_safe); - end - else - DLLLoaded := False; -end; - -procedure Deinit; -begin - if not DLLLoaded then - Exit; - FreeLibrary(DLLHandle); -end; - function LZ4F_decompress_safe(source: Pointer; dest: Pointer; - compressedSize: Integer; maxDecompressedSize: Integer): Integer; + sourceSize: Integer; destSize: Integer; compressedSize: PInteger; + blockSize: PInteger): Integer; var ctx: LZ4F_dctx; - srcSizePtr, dstSizePtr: size_t; + fi: LZ4F_frameInfo_t; + srcSizePtr, dstSizePtr, srcSizePtr2: size_t; begin Result := 0; + if Assigned(compressedSize) then + compressedSize^ := 0; + if Assigned(blockSize) then + blockSize^ := 4; if NativeUInt(LZ4F_createDecompressionContext(ctx)) = 0 then try - srcSizePtr := compressedSize; - dstSizePtr := maxDecompressedSize; + srcSizePtr := sourceSize; + dstSizePtr := destSize; try + FillChar(fi, SizeOf(LZ4F_frameInfo_t), 0); + srcSizePtr2 := sourceSize; if LZ4F_decompress(ctx, dest, dstSizePtr, source, srcSizePtr, nil) = 0 then + begin + LZ4F_getFrameInfo(ctx, fi, source, srcSizePtr2); + if Assigned(compressedSize) then + compressedSize^ := srcSizePtr; + if Assigned(blockSize) then + blockSize^ := Max(4, Integer(fi.blockSizeID)); Result := dstSizePtr; + end; finally LZ4F_freeDecompressionContext(ctx); end; @@ -151,6 +159,157 @@ begin end; end; +function LZ4_compress_block(src, dst: Pointer; + srcSize, dstCapacity: Integer): Integer; +const + blockSize = 64 * 1024; +const + BuffSize = 256 * 1024; +var + Buff: array [0 .. BuffSize - 1] of byte; + ctx: PLZ4_stream_t; + Pos1, Pos2, Res: Integer; + X, Y: Integer; +begin + Result := 0; + ctx := LZ4_createStream; + LZ4_resetStream(ctx); + Pos1 := 0; + Pos2 := 0; + try + while (Pos1 < srcSize) and (Pos2 < dstCapacity) do + begin + X := Min(srcSize - Pos1, blockSize); + Y := dstCapacity - Pos2; + Res := LZ4_compress_fast_continue(ctx, PByte(src) + Pos1, @Buff[0], X, + BuffSize, 1); + if Res <= 0 then + begin + LZ4_freeStream(ctx); + exit(-Pos2); + end; + Move(Buff[0], (PByte(dst) + Pos2)^, Res); + Inc(Pos1, X); + Inc(Pos2, Res); + end; + finally + LZ4_freeStream(ctx); + end; + Result := Pos2; +end; + +function UnravelEncode(InBuff: Pointer; InSize: Integer; OutBuff: Pointer; + OutSize: Integer): Integer; +const + blockSize = 65536; +var + ctx: PLZ4_streamHC_t; + Pos1, Pos2, Res: Integer; + X, Y: Integer; +begin + Result := 0; + ctx := LZ4_createStreamHC; + LZ4_resetStreamHC(ctx, 9); + Pos1 := 0; + Pos2 := 0; + try + while (Pos1 < InSize) do + begin + X := Min(InSize - Pos1, blockSize); + Y := OutSize - (Pos2 + Integer.Size); + Res := LZ4_compress_HC_continue(ctx, PByte(InBuff) + Pos1, + PByte(OutBuff) + Pos2 + Integer.Size, X, Y); + if Res <= 0 then + begin + LZ4_freeStreamHC(ctx); + exit(-Pos2); + end; + PInteger(PByte(OutBuff) + Pos2)^ := Res; + Inc(Pos1, X); + Inc(Pos2, Res + Integer.Size); + end; + finally + LZ4_freeStreamHC(ctx); + end; + Result := Pos2; +end; + +function UnravelDecode(InBuff: Pointer; InSize: Integer; OutBuff: Pointer; + OutSize: Integer): Integer; +const + blockSize = 65536; +var + ctx: PLZ4_streamDecode_t; + Pos1, Pos2, Res: Integer; +begin + Result := 0; + ctx := LZ4_createStreamDecode; + Pos1 := 0; + Pos2 := 0; + try + while (Pos1 < InSize) and (Pos2 < OutSize) do + begin + Res := LZ4_decompress_safe_continue(ctx, PByte(InBuff) + Pos1 + + Integer.Size, PByte(OutBuff) + Pos2, PInteger(PByte(InBuff) + Pos1)^, + Min(OutSize - Pos2, blockSize)); + if Res <= 0 then + begin + LZ4_freeStreamDecode(ctx); + exit(-Pos2); + end; + Inc(Pos1, PInteger(PByte(InBuff) + Pos1)^ + Integer.Size); + Inc(Pos2, Res); + end; + finally + LZ4_freeStreamDecode(ctx); + end; + Result := Pos2; +end; + +var + Lib: TLibImport; + +procedure Init(Filename: String); +begin + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + Filename); + if Lib.Loaded then + begin + @LZ4_decompress_safe := Lib.GetProcAddr('LZ4_decompress_safe'); + @LZ4_decompress_fast := Lib.GetProcAddr('LZ4_decompress_fast'); + @LZ4_compress_default := Lib.GetProcAddr('LZ4_compress_default'); + @LZ4_compress_fast := Lib.GetProcAddr('LZ4_compress_fast'); + @LZ4_compress_HC := Lib.GetProcAddr('LZ4_compress_HC'); + @LZ4_compressHC2 := Lib.GetProcAddr('LZ4_compressHC2'); + @LZ4F_compressFrame := Lib.GetProcAddr('LZ4F_compressFrame'); + @LZ4F_compressFrameBound := Lib.GetProcAddr('LZ4F_compressFrameBound'); + @LZ4F_createDecompressionContext := + Lib.GetProcAddr('LZ4F_createDecompressionContext'); + @LZ4F_freeDecompressionContext := + Lib.GetProcAddr('LZ4F_freeDecompressionContext'); + @LZ4F_decompress := Lib.GetProcAddr('LZ4F_decompress'); + @LZ4F_getFrameInfo := Lib.GetProcAddr('LZ4F_getFrameInfo'); + @LZ4_createStreamDecode := Lib.GetProcAddr('LZ4_createStreamDecode'); + @LZ4_freeStreamDecode := Lib.GetProcAddr('LZ4_freeStreamDecode'); + @LZ4_decompress_safe_continue := + Lib.GetProcAddr('LZ4_decompress_safe_continue'); + @LZ4_createStream := Lib.GetProcAddr('LZ4_createStream'); + @LZ4_freeStream := Lib.GetProcAddr('LZ4_freeStream'); + @LZ4_resetStream := Lib.GetProcAddr('LZ4_resetStream'); + @LZ4_compress_fast_continue := + Lib.GetProcAddr('LZ4_compress_fast_continue'); + @LZ4_createStreamHC := Lib.GetProcAddr('LZ4_createStreamHC'); + @LZ4_freeStreamHC := Lib.GetProcAddr('LZ4_freeStreamHC'); + @LZ4_resetStreamHC := Lib.GetProcAddr('LZ4_resetStreamHC'); + @LZ4_compress_HC_continue := Lib.GetProcAddr('LZ4_compress_HC_continue'); + DLLLoaded := Assigned(LZ4_decompress_safe); + end; +end; + +procedure Deinit; +begin + Lib.Free; +end; + const DLLParam = '--lz4='; diff --git a/imports/LZODLL.pas b/imports/LZODLL.pas index 15d2667..7c82711 100644 --- a/imports/LZODLL.pas +++ b/imports/LZODLL.pas @@ -3,6 +3,7 @@ unit LZODLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils; @@ -60,83 +61,36 @@ var dst: Pointer; dst_len: PNativeUInt; wrkmem: Pointer): integer; cdecl; lzo2a_decompress_safe: function(const src: Pointer; src_len: NativeUInt; dst: Pointer; dst_len: PNativeUInt): integer cdecl; - lzopro_lzo1x_w03_15_compress: function(const src: Pointer; - src_len: NativeUInt; dst: Pointer; dst_len: PNativeUInt; wrkmem: Pointer) - : integer; cdecl; - lzopro_lzo1x_99_compress: function(const src; src_len: integer; var dst; - var dst_len; var cb; compression_level: integer): integer; cdecl; DLLLoaded: Boolean = False; -function lzo1x_99_compress(const src: Pointer; src_len: NativeUInt; - dst: Pointer; dst_len: PNativeUInt; compression_level: integer): integer; - implementation var - DLLHandle: THandle; - -procedure nfree(self: Pointer; ptr: Pointer); cdecl; -begin - FreeMem(ptr); -end; - -function nalloc(self: Pointer; items, size: LongWord): Pointer; cdecl; -var - p: Pointer; -begin - GetMem(p, size * items); - Result := p; -end; - -procedure nprogress(self: Pointer; a, b: LongWord; c: integer); cdecl; -begin -end; - -function lzo1x_99_compress(const src: Pointer; src_len: NativeUInt; - dst: Pointer; dst_len: PNativeUInt; compression_level: integer): integer; -var - mycb: lzo_callback_t; -begin - mycb.nalloc := nalloc; - mycb.nfree := nfree; - mycb.nprogress := nprogress; - Result := lzopro_lzo1x_99_compress(src^, src_len, dst^, dst_len^, mycb, - compression_level); -end; + Lib: TLibImport; procedure Init(Filename: String); begin - if DLLLoaded then - Exit; - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + Filename)); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + Filename); + if Lib.Loaded then begin - @lzo1x_1_compress := GetProcAddress(DLLHandle, 'lzo1x_1_compress'); - @lzo1x_1_11_compress := GetProcAddress(DLLHandle, 'lzo1x_1_11_compress'); - @lzo1x_1_12_compress := GetProcAddress(DLLHandle, 'lzo1x_1_12_compress'); - @lzo1x_1_15_compress := GetProcAddress(DLLHandle, 'lzo1x_1_15_compress'); - @lzo1x_999_compress := GetProcAddress(DLLHandle, 'lzo1x_999_compress'); - @lzo1x_999_compress_level := GetProcAddress(DLLHandle, - 'lzo1x_999_compress_level'); - @lzo1x_decompress_safe := GetProcAddress(DLLHandle, - 'lzo1x_decompress_safe'); - @lzo1c_999_compress := GetProcAddress(DLLHandle, 'lzo1c_999_compress'); - @lzo1c_decompress_safe := GetProcAddress(DLLHandle, - 'lzo1c_decompress_safe'); - @lzo2a_999_compress := GetProcAddress(DLLHandle, 'lzo2a_999_compress'); - @lzo2a_decompress_safe := GetProcAddress(DLLHandle, - 'lzo2a_decompress_safe'); + @lzo1x_1_compress := Lib.GetProcAddr('lzo1x_1_compress'); + @lzo1x_1_11_compress := Lib.GetProcAddr('lzo1x_1_11_compress'); + @lzo1x_1_12_compress := Lib.GetProcAddr('lzo1x_1_12_compress'); + @lzo1x_1_15_compress := Lib.GetProcAddr('lzo1x_1_15_compress'); + @lzo1x_999_compress := Lib.GetProcAddr('lzo1x_999_compress'); + @lzo1x_999_compress_level := Lib.GetProcAddr('lzo1x_999_compress_level'); + @lzo1x_decompress_safe := Lib.GetProcAddr('lzo1x_decompress_safe'); + @lzo1c_999_compress := Lib.GetProcAddr('lzo1c_999_compress'); + @lzo1c_decompress_safe := Lib.GetProcAddr('lzo1c_decompress_safe'); + @lzo2a_999_compress := Lib.GetProcAddr('lzo2a_999_compress'); + @lzo2a_decompress_safe := Lib.GetProcAddr('lzo2a_decompress_safe'); DLLLoaded := Assigned(lzo1x_decompress_safe); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - Exit; - FreeLibrary(DLLHandle); + Lib.Free; end; const diff --git a/imports/OodleDLL.pas b/imports/OodleDLL.pas index 865c545..eb0c18a 100644 --- a/imports/OodleDLL.pas +++ b/imports/OodleDLL.pas @@ -3,6 +3,7 @@ unit OodleDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Types, System.IOUtils; @@ -72,57 +73,42 @@ function OodleLZ_GetCompressedBufferSizeNeeded(compressor: Byte; implementation var - DLLHandle: THandle; + Lib: TLibImport; OldCompress, OldCompressOptions_GetDefault, OldGetCompressedBufferSizeNeeded: Boolean; - DLLs: TStringDynArray; procedure Init(Filename: String); var I: Integer; C: Cardinal; begin - if DLLLoaded then - Exit; - DLLs := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), 'oo2core*.dll', - TSearchOption.soTopDirectoryOnly); - Insert(ExtractFilePath(ParamStr(0)) + Filename, DLLs, 0); - for I := Low(DLLs) to High(DLLs) do - begin - DLLHandle := LoadLibrary(PChar(DLLs[I])); - if DLLHandle >= 32 then - break; - end; - if DLLHandle < 32 then - begin - DLLs := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), 'oo2ext*.dll', - TSearchOption.soTopDirectoryOnly); - for I := Low(DLLs) to High(DLLs) do + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + Filename); + if not Lib.Loaded then + for I := 3 to 9 do begin - DLLHandle := LoadLibrary(PChar(DLLs[I])); - if DLLHandle >= 32 then + Lib.Free; + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'oo2core_' + + I.ToString + '_win64.dll'); + if Lib.Loaded then break; end; - end; - if DLLHandle < 32 then - begin - DLLs := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), 'oodle2*.dll', - TSearchOption.soTopDirectoryOnly); - for I := Low(DLLs) to High(DLLs) do + if not Lib.Loaded then + for I := 3 to 9 do begin - DLLHandle := LoadLibrary(PChar(DLLs[I])); - if DLLHandle >= 32 then + Lib.Free; + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'oo2ext_' + + I.ToString + '_win64.dll'); + if Lib.Loaded then break; end; - end; - if DLLHandle >= 32 then + if Lib.Loaded then begin - Oodle_CheckVersion := GetProcAddress(DLLHandle, 'Oodle_CheckVersion'); + Oodle_CheckVersion := Lib.GetProcAddr('Oodle_CheckVersion'); if not Assigned(Oodle_CheckVersion) then for I := 0 to 32 do begin - @Oodle_CheckVersion := GetProcAddress(DLLHandle, - PChar('_Oodle_CheckVersion@' + (I * 2).ToString)); + @Oodle_CheckVersion := + Lib.GetProcAddr(PAnsiChar('_Oodle_CheckVersion@' + (I * 2).ToString)); if Assigned(Oodle_CheckVersion) then break; end; @@ -131,61 +117,57 @@ begin OldCompress := LongRec(C).Hi < $2E06; OldGetCompressedBufferSizeNeeded := LongRec(C).Hi < $2E08; OldCompressOptions_GetDefault := LongRec(C).Hi < $2E08; - @OodleLZ_Compress_1 := GetProcAddress(DLLHandle, 'OodleLZ_Compress'); + @OodleLZ_Compress_1 := Lib.GetProcAddr('OodleLZ_Compress'); if not Assigned(OodleLZ_Compress_1) then for I := 0 to 32 do begin - @OodleLZ_Compress_1 := GetProcAddress(DLLHandle, - PChar('_OodleLZ_Compress@' + (I * 2).ToString)); + @OodleLZ_Compress_1 := + Lib.GetProcAddr(PAnsiChar('_OodleLZ_Compress@' + (I * 2).ToString)); if Assigned(OodleLZ_Compress_1) then break; end; @OodleLZ_Compress_2 := @OodleLZ_Compress_1; - OodleLZ_Decompress := GetProcAddress(DLLHandle, 'OodleLZ_Decompress'); + OodleLZ_Decompress := Lib.GetProcAddr('OodleLZ_Decompress'); if not Assigned(OodleLZ_Decompress) then for I := 0 to 32 do begin - @OodleLZ_Decompress := GetProcAddress(DLLHandle, - PChar('_OodleLZ_Decompress@' + (I * 2).ToString)); + @OodleLZ_Decompress := + Lib.GetProcAddr(PAnsiChar('_OodleLZ_Decompress@' + (I * 2).ToString)); if Assigned(OodleLZ_Decompress) then break; end; - OodleLZ_CompressOptions_GetDefault_1 := GetProcAddress(DLLHandle, - 'OodleLZ_CompressOptions_GetDefault'); + OodleLZ_CompressOptions_GetDefault_1 := + Lib.GetProcAddr('OodleLZ_CompressOptions_GetDefault'); if not Assigned(OodleLZ_CompressOptions_GetDefault_1) then for I := 0 to 32 do begin @OodleLZ_CompressOptions_GetDefault_1 := - GetProcAddress(DLLHandle, PChar('_OodleLZ_CompressOptions_GetDefault@' - + (I * 2).ToString)); + Lib.GetProcAddr(PAnsiChar('_OodleLZ_CompressOptions_GetDefault@' + + (I * 2).ToString)); if Assigned(OodleLZ_CompressOptions_GetDefault_1) then break; end; @OodleLZ_CompressOptions_GetDefault_2 := @OodleLZ_CompressOptions_GetDefault_1; OodleLZ_GetCompressedBufferSizeNeeded_1 := - GetProcAddress(DLLHandle, 'OodleLZ_GetCompressedBufferSizeNeeded'); + Lib.GetProcAddr('OodleLZ_GetCompressedBufferSizeNeeded'); if not Assigned(OodleLZ_GetCompressedBufferSizeNeeded_1) then for I := 0 to 32 do begin @OodleLZ_GetCompressedBufferSizeNeeded_1 := - GetProcAddress(DLLHandle, - PChar('_OodleLZ_GetCompressedBufferSizeNeeded@' + (I * 2).ToString)); + Lib.GetProcAddr(PAnsiChar('_OodleLZ_GetCompressedBufferSizeNeeded@' + + (I * 2).ToString)); if Assigned(OodleLZ_GetCompressedBufferSizeNeeded_1) then break; end; @OodleLZ_GetCompressedBufferSizeNeeded_2 := @OodleLZ_GetCompressedBufferSizeNeeded_1; - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - Exit; - FreeLibrary(DLLHandle); + Lib.Free; end; function OodleLZ_Compress(compressor: Integer; rawBuf: Pointer; @@ -228,7 +210,7 @@ var initialization -DLLFile := 'oodle.dll'; +DLLFile := 'oo2core_9_win64.dll'; for I := 1 to ParamCount do if ParamStr(I).StartsWith(DLLParam) then begin diff --git a/imports/PackJPGDLL.pas b/imports/PackJPGDLL.pas index b1dbe99..bf63698 100644 --- a/imports/PackJPGDLL.pas +++ b/imports/PackJPGDLL.pas @@ -3,6 +3,7 @@ unit PackJPGDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -25,35 +26,28 @@ var implementation var - DLLHandle: THandle; + Lib: TLibImport; procedure Init; begin - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + - 'packjpg_dll.dll')); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'packjpg_dll.dll'); + if Lib.Loaded then begin - @pjglib_convert_stream2stream := GetProcAddress(DLLHandle, - 'pjglib_convert_stream2stream'); - @pjglib_convert_file2file := GetProcAddress(DLLHandle, - 'pjglib_convert_file2file'); - @pjglib_convert_stream2mem := GetProcAddress(DLLHandle, - 'pjglib_convert_stream2mem'); - @pjglib_init_streams := GetProcAddress(DLLHandle, 'pjglib_init_streams'); - @pjglib_version_info := GetProcAddress(DLLHandle, 'pjglib_version_info'); - @pjglib_short_name := GetProcAddress(DLLHandle, 'pjglib_short_name'); + @pjglib_convert_stream2stream := + Lib.GetProcAddr('pjglib_convert_stream2stream'); + @pjglib_convert_file2file := Lib.GetProcAddr('pjglib_convert_file2file'); + @pjglib_convert_stream2mem := Lib.GetProcAddr('pjglib_convert_stream2mem'); + @pjglib_init_streams := Lib.GetProcAddr('pjglib_init_streams'); + @pjglib_version_info := Lib.GetProcAddr('pjglib_version_info'); + @pjglib_short_name := Lib.GetProcAddr('pjglib_short_name'); DLLLoaded := Assigned(pjglib_init_streams) and Assigned(pjglib_convert_stream2stream); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - FreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/PreflateDLL.pas b/imports/PreflateDLL.pas index 7f2d949..3bd6702 100644 --- a/imports/PreflateDLL.pas +++ b/imports/PreflateDLL.pas @@ -3,6 +3,7 @@ unit PreflateDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -18,27 +19,22 @@ var implementation var - DLLHandle: THandle; + Lib: TLibImport; procedure Init; begin - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + - 'preflate_dll.dll')); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'preflate_dll.dll'); + if Lib.Loaded then begin - @preflate_decode := GetProcAddress(DLLHandle, 'decode'); - @preflate_reencode := GetProcAddress(DLLHandle, 'reencode'); + @preflate_decode := Lib.GetProcAddr('decode'); + @preflate_reencode := Lib.GetProcAddr('reencode'); DLLLoaded := Assigned(preflate_decode) and Assigned(preflate_reencode); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - FreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/ReflateDLL.pas b/imports/ReflateDLL.pas index 72efb9c..7b050fa 100644 --- a/imports/ReflateDLL.pas +++ b/imports/ReflateDLL.pas @@ -3,6 +3,7 @@ unit ReflateDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -25,41 +26,35 @@ var implementation var - DLLHandle1, DLLHandle2: THandle; + Lib1, Lib2: TLibImport; procedure Init; begin - DLLHandle1 := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + - 'RAW2HIF_DLL.DLL')); - DLLHandle2 := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + - 'HIF2RAW_DLL.DLL')); - if (DLLHandle1 >= 32) and (DLLHandle2 >= 32) then + Lib1 := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'RAW2HIF_DLL.DLL'); + Lib2 := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'HIF2RAW_DLL.DLL'); + if Lib1.Loaded and Lib2.Loaded then begin - @raw2hif_Alloc := GetProcAddress(DLLHandle1, 'raw2hif_Alloc'); - @raw2hif_Free := GetProcAddress(DLLHandle1, 'raw2hif_Free'); - @raw2hif_Init := GetProcAddress(DLLHandle1, 'raw2hif_Init'); - @raw2hif_Loop := GetProcAddress(DLLHandle1, 'raw2hif_Loop'); - @raw2hif_getoutlen := GetProcAddress(DLLHandle1, 'raw2hif_getoutlen'); - @raw2hif_getou2len := GetProcAddress(DLLHandle1, 'raw2hif_getou2len'); - @raw2hif_addbuf := GetProcAddress(DLLHandle1, 'raw2hif_addbuf'); - @hif2raw_Alloc := GetProcAddress(DLLHandle2, 'hif2raw_Alloc'); - @hif2raw_Free := GetProcAddress(DLLHandle2, 'hif2raw_Free'); - @hif2raw_Init := GetProcAddress(DLLHandle2, 'hif2raw_Init'); - @hif2raw_Loop := GetProcAddress(DLLHandle2, 'hif2raw_Loop'); - @hif2raw_getoutlen := GetProcAddress(DLLHandle2, 'hif2raw_getoutlen'); - @hif2raw_addbuf := GetProcAddress(DLLHandle2, 'hif2raw_addbuf'); + @raw2hif_Alloc := Lib1.GetProcAddr('raw2hif_Alloc'); + @raw2hif_Free := Lib1.GetProcAddr('raw2hif_Free'); + @raw2hif_Init := Lib1.GetProcAddr('raw2hif_Init'); + @raw2hif_Loop := Lib1.GetProcAddr('raw2hif_Loop'); + @raw2hif_getoutlen := Lib1.GetProcAddr('raw2hif_getoutlen'); + @raw2hif_getou2len := Lib1.GetProcAddr('raw2hif_getou2len'); + @raw2hif_addbuf := Lib1.GetProcAddr('raw2hif_addbuf'); + @hif2raw_Alloc := Lib2.GetProcAddr('hif2raw_Alloc'); + @hif2raw_Free := Lib2.GetProcAddr('hif2raw_Free'); + @hif2raw_Init := Lib2.GetProcAddr('hif2raw_Init'); + @hif2raw_Loop := Lib2.GetProcAddr('hif2raw_Loop'); + @hif2raw_getoutlen := Lib2.GetProcAddr('hif2raw_getoutlen'); + @hif2raw_addbuf := Lib2.GetProcAddr('hif2raw_addbuf'); DLLLoaded := Assigned(raw2hif_Alloc) and Assigned(hif2raw_Alloc); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - FreeLibrary(DLLHandle1); - FreeLibrary(DLLHandle2); + Lib1.Free; + Lib2.Free; end; initialization diff --git a/imports/XDeltaDLL.pas b/imports/XDeltaDLL.pas index c7ff24e..41fbe39 100644 --- a/imports/XDeltaDLL.pas +++ b/imports/XDeltaDLL.pas @@ -3,7 +3,7 @@ unit XDeltaDLL; interface uses - MemoryModule, + LibImport, WinAPI.Windows, System.SysUtils, System.Classes; @@ -37,34 +37,25 @@ var implementation -uses - Utils; - var - DLLStream: TResourceStream; - DLLHandle: TMemoryModule; + Lib: TLibImport; procedure Init; begin - DLLStream := TResourceStream.Create(HInstance, 'xdelta3_dll', RT_RCDATA); - DLLHandle := MemoryLoadLibary(DLLStream.Memory); - if Assigned(DLLHandle) then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'xdelta3_dll.dll'); + if Lib.Loaded then begin DLLLoaded := True; - @xd3_encode := MemoryGetProcAddress(DLLHandle, 'xd3_encode'); + @xd3_encode := Lib.GetProcAddr('xd3_encode'); Assert(@xd3_encode <> nil); - @xd3_decode := MemoryGetProcAddress(DLLHandle, 'xd3_decode'); + @xd3_decode := Lib.GetProcAddr('xd3_decode'); Assert(@xd3_decode <> nil); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - exit; - MemoryFreeLibrary(DLLHandle); + Lib.Free; end; initialization diff --git a/imports/ZLibDLL.pas b/imports/ZLibDLL.pas index e7387fb..68fd4ff 100644 --- a/imports/ZLibDLL.pas +++ b/imports/ZLibDLL.pas @@ -3,6 +3,7 @@ unit ZLibDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils, System.Types, System.IOUtils, System.ZLib; @@ -86,55 +87,8 @@ function inflateReset(var strm: z_stream): integer; implementation var - DLLHandle: THandle; + Lib: TLibImport; WinAPIDLL: boolean; - DLLs: TStringDynArray; - -procedure Init(Filename: String); -var - I: integer; -begin - if DLLLoaded then - Exit; - DLLs := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), 'zlib*.dll', - TSearchOption.soTopDirectoryOnly); - Insert(ExtractFilePath(ParamStr(0)) + 'zlib.dll', DLLs, Length(DLLs)); - Insert(ExtractFilePath(ParamStr(0)) + Filename, DLLs, 0); - for I := Low(DLLs) to High(DLLs) do - begin - DLLHandle := LoadLibrary(PChar(DLLs[I])); - if (DLLHandle >= 32) and Assigned(GetProcAddress(DLLHandle, 'zlibVersion')) - then - break; - end; - if DLLHandle >= 32 then - begin - DLLLoaded := True; - @_zlibVersion := GetProcAddress(DLLHandle, 'zlibVersion'); - @_zlibCompileFlags := GetProcAddress(DLLHandle, 'zlibCompileFlags'); - DLLLoaded := Assigned(_zlibVersion) and Assigned(_zlibCompileFlags); - if DLLLoaded then - begin - WinAPIDLL := _zlibCompileFlags and $400 = $400; - if WinAPIDLL then - begin - @s_deflateInit2_ := GetProcAddress(DLLHandle, 'deflateInit2_'); - @s_deflate := GetProcAddress(DLLHandle, 'deflate'); - @s_deflateEnd := GetProcAddress(DLLHandle, 'deflateEnd'); - @s_deflateReset := GetProcAddress(DLLHandle, 'deflateReset'); - end - else - begin - @c_deflateInit2_ := GetProcAddress(DLLHandle, 'deflateInit2_'); - @c_deflate := GetProcAddress(DLLHandle, 'deflate'); - @c_deflateEnd := GetProcAddress(DLLHandle, 'deflateEnd'); - @c_deflateReset := GetProcAddress(DLLHandle, 'deflateReset'); - end; - end; - end - else - DLLLoaded := False; -end; function deflateInit2(var strm: z_stream; level, method, windowBits, memLevel, strategy: integer): integer; @@ -192,11 +146,49 @@ begin Result := System.ZLib.inflateReset(strm); end; +procedure Init(Filename: String); +begin + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + Filename); + if not(Lib.Loaded and Assigned(Lib.GetProcAddr('zlibVersion'))) then + begin + Lib.Free; + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'zlibwapi.dll'); + end; + if not(Lib.Loaded and Assigned(Lib.GetProcAddr('zlibVersion'))) then + begin + Lib.Free; + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + 'zlib1.dll'); + end; + if Lib.Loaded and Assigned(Lib.GetProcAddr('zlibVersion')) then + begin + DLLLoaded := True; + @_zlibVersion := Lib.GetProcAddr('zlibVersion'); + @_zlibCompileFlags := Lib.GetProcAddr('zlibCompileFlags'); + DLLLoaded := Assigned(_zlibVersion) and Assigned(_zlibCompileFlags); + if DLLLoaded then + begin + WinAPIDLL := _zlibCompileFlags and $400 = $400; + if WinAPIDLL then + begin + @s_deflateInit2_ := Lib.GetProcAddr('deflateInit2_'); + @s_deflate := Lib.GetProcAddr('deflate'); + @s_deflateEnd := Lib.GetProcAddr('deflateEnd'); + @s_deflateReset := Lib.GetProcAddr('deflateReset'); + end + else + begin + @c_deflateInit2_ := Lib.GetProcAddr('deflateInit2_'); + @c_deflate := Lib.GetProcAddr('deflate'); + @c_deflateEnd := Lib.GetProcAddr('deflateEnd'); + @c_deflateReset := Lib.GetProcAddr('deflateReset'); + end; + end; + end; +end; + procedure Deinit; begin - if not DLLLoaded then - Exit; - FreeLibrary(DLLHandle); + Lib.Free; end; const diff --git a/imports/ZSTDDLL.pas b/imports/ZSTDDLL.pas index 480e454..522324b 100644 --- a/imports/ZSTDDLL.pas +++ b/imports/ZSTDDLL.pas @@ -3,6 +3,7 @@ unit ZSTDDLL; interface uses + LibImport, WinAPI.Windows, System.SysUtils; @@ -142,54 +143,45 @@ begin end; var - DLLHandle: THandle; + Lib: TLibImport; procedure Init(Filename: String); begin - if DLLLoaded then - Exit; - DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + Filename)); - if DLLHandle >= 32 then + Lib := TLibImport.Create(ExtractFilePath(ParamStr(0)) + Filename); + if Lib.Loaded then begin - @ZSTD_compress := GetProcAddress(DLLHandle, 'ZSTD_compress'); - @ZSTD_compress2 := GetProcAddress(DLLHandle, 'ZSTD_compress2'); - @ZSTD_decompress := GetProcAddress(DLLHandle, 'ZSTD_decompress'); - @ZSTD_findFrameCompressedSize := GetProcAddress(DLLHandle, - 'ZSTD_findFrameCompressedSize'); - @ZSTD_findDecompressedSize := GetProcAddress(DLLHandle, - 'ZSTD_findDecompressedSize'); - @ZSTD_createCCtx := GetProcAddress(DLLHandle, 'ZSTD_createCCtx'); - @ZSTD_freeCCtx := GetProcAddress(DLLHandle, 'ZSTD_freeCCtx'); - @ZSTD_CCtx_reset := GetProcAddress(DLLHandle, 'ZSTD_CCtx_reset'); - @ZSTD_CCtx_setParameter := GetProcAddress(DLLHandle, - 'ZSTD_CCtx_setParameter'); - @ZSTD_createDCtx := GetProcAddress(DLLHandle, 'ZSTD_createDCtx'); - @ZSTD_freeDCtx := GetProcAddress(DLLHandle, 'ZSTD_freeDCtx'); - @ZSTD_createCDict := GetProcAddress(DLLHandle, 'ZSTD_createCDict'); - @ZSTD_freeCDict := GetProcAddress(DLLHandle, 'ZSTD_freeCDict'); - @ZSTD_compressCCtx := GetProcAddress(DLLHandle, 'ZSTD_compressCCtx'); - @ZSTD_createDDict := GetProcAddress(DLLHandle, 'ZSTD_createDDict'); - @ZSTD_freeDDict := GetProcAddress(DLLHandle, 'ZSTD_freeDDict'); - @ZSTD_decompressDCtx := GetProcAddress(DLLHandle, 'ZSTD_decompressDCtx'); - @ZSTD_compress_usingCDict := GetProcAddress(DLLHandle, - 'ZSTD_compress_usingCDict'); - @ZSTD_decompress_usingDDict := GetProcAddress(DLLHandle, - 'ZSTD_decompress_usingDDict'); - @ZSTD_initCStream := GetProcAddress(DLLHandle, 'ZSTD_initCStream'); - @ZSTD_compressStream := GetProcAddress(DLLHandle, 'ZSTD_compressStream'); - @ZSTD_flushStream := GetProcAddress(DLLHandle, 'ZSTD_flushStream'); - @ZSTD_endStream := GetProcAddress(DLLHandle, 'ZSTD_endStream'); + @ZSTD_compress := Lib.GetProcAddr('ZSTD_compress'); + @ZSTD_compress2 := Lib.GetProcAddr('ZSTD_compress2'); + @ZSTD_decompress := Lib.GetProcAddr('ZSTD_decompress'); + @ZSTD_findFrameCompressedSize := + Lib.GetProcAddr('ZSTD_findFrameCompressedSize'); + @ZSTD_findDecompressedSize := Lib.GetProcAddr('ZSTD_findDecompressedSize'); + @ZSTD_createCCtx := Lib.GetProcAddr('ZSTD_createCCtx'); + @ZSTD_freeCCtx := Lib.GetProcAddr('ZSTD_freeCCtx'); + @ZSTD_CCtx_reset := Lib.GetProcAddr('ZSTD_CCtx_reset'); + @ZSTD_CCtx_setParameter := Lib.GetProcAddr('ZSTD_CCtx_setParameter'); + @ZSTD_createDCtx := Lib.GetProcAddr('ZSTD_createDCtx'); + @ZSTD_freeDCtx := Lib.GetProcAddr('ZSTD_freeDCtx'); + @ZSTD_createCDict := Lib.GetProcAddr('ZSTD_createCDict'); + @ZSTD_freeCDict := Lib.GetProcAddr('ZSTD_freeCDict'); + @ZSTD_compressCCtx := Lib.GetProcAddr('ZSTD_compressCCtx'); + @ZSTD_createDDict := Lib.GetProcAddr('ZSTD_createDDict'); + @ZSTD_freeDDict := Lib.GetProcAddr('ZSTD_freeDDict'); + @ZSTD_decompressDCtx := Lib.GetProcAddr('ZSTD_decompressDCtx'); + @ZSTD_compress_usingCDict := Lib.GetProcAddr('ZSTD_compress_usingCDict'); + @ZSTD_decompress_usingDDict := + Lib.GetProcAddr('ZSTD_decompress_usingDDict'); + @ZSTD_initCStream := Lib.GetProcAddr('ZSTD_initCStream'); + @ZSTD_compressStream := Lib.GetProcAddr('ZSTD_compressStream'); + @ZSTD_flushStream := Lib.GetProcAddr('ZSTD_flushStream'); + @ZSTD_endStream := Lib.GetProcAddr('ZSTD_endStream'); DLLLoaded := Assigned(ZSTD_compress) and Assigned(ZSTD_decompress); - end - else - DLLLoaded := False; + end; end; procedure Deinit; begin - if not DLLLoaded then - Exit; - FreeLibrary(DLLHandle); + Lib.Free; end; const diff --git a/precompressor/PrecompEXE.pas b/precompressor/PrecompEXE.pas index 9dfca77..589382b 100644 --- a/precompressor/PrecompEXE.pas +++ b/precompressor/PrecompEXE.pas @@ -274,7 +274,6 @@ begin S := ReplaceText(S, '', StreamInfo^.OldSize.ToString); S := ReplaceText(S, '', StreamInfo^.NewSize.ToString); Res := 0; - Res := 0; if ContainsText(S, '') and Funcs^.GetResource(StreamInfo^.Resource, nil, @Res) and (Res > 0) then begin @@ -680,7 +679,7 @@ var Bytes: TBytes; Ini: TMemIniFile; SL: TStringList; - ExeStruct: PExeStruct; + ExeStruct: TExeStruct; Y, Z: Integer; List: TStringDynArray; @@ -699,80 +698,84 @@ begin GetCmdStr(Ini.ReadString(SL[I], 'Decode', ''), 0)) then for K := Low(List) to High(List) do begin - New(ExeStruct); Insert(List[K], Codec.Names, Length(Codec.Names)); - ExeStruct^.Name := List[K]; - Bytes := BytesOf(ExeStruct^.Name); - ExeStruct^.ID := Utils.Hash32(0, @Bytes[0], Length(Bytes)); + ExeStruct.Name := List[K]; + Bytes := BytesOf(ExeStruct.Name); + ExeStruct.ID := Utils.Hash32(0, @Bytes[0], Length(Bytes)); for X := 0 to 1 do begin - ExeStruct^.IsLib[X] := False; + ExeStruct.IsLib[X] := False; if X = 0 then S1 := Ini.ReadString(SL[I], 'Encode', '') else S1 := Ini.ReadString(SL[I], 'Decode', ''); S1 := ReplaceText(S1, '', List[K]); - ExeStruct^.Exec[X] := ExtractFilePath(Utils.GetModuleName) + + ExeStruct.Exec[X] := ExtractFilePath(Utils.GetModuleName) + GetCmdStr(S1, 0); - ExeStruct^.Param[X] := ''; - ExeStruct^.Mode[X] := 0; + ExeStruct.Param[X] := ''; + ExeStruct.Mode[X] := 0; for J := 1 to GetCmdCount(S1) - 1 do begin S2 := GetCmdStr(S1, J); if ContainsText(S2, '') then begin - SetBits(ExeStruct^.Mode[X], STDIO_MODE, 0, 2); - ExeStruct^.IsLib[X] := True; + SetBits(ExeStruct.Mode[X], STDIO_MODE, 0, 2); + ExeStruct.IsLib[X] := True; continue; end else if ContainsText(S2, '') or ContainsText(S2, '[stdin]') then begin - SetBits(ExeStruct^.Mode[X], 1, 0, 1); + SetBits(ExeStruct.Mode[X], 1, 0, 1); continue; end else if ContainsText(S2, '') or ContainsText(S2, '[stdout]') then begin - SetBits(ExeStruct^.Mode[X], 1, 1, 1); + SetBits(ExeStruct.Mode[X], 1, 1, 1); continue; end else if ContainsText(S2, '') or ContainsText(S2, '[filein]') then begin S3 := IfThen(X = 0, FILE_IN, FILE_OUT); - SetBits(ExeStruct^.Mode[X], 0, 0, 1); + SetBits(ExeStruct.Mode[X], 0, 0, 1); if ContainsText(S2, '') then - ExeStruct^.InFile[X] := ExtractStr('', S2) + ExeStruct.InFile[X] := ExtractStr('', S2) else - ExeStruct^.InFile[X] := ExtractStr('[filein]', S2); - S2 := ReplaceText(S2, ExeStruct^.InFile[X], S3); - ExeStruct^.InFile[X] := ReplaceText(ExeStruct^.InFile[X], + ExeStruct.InFile[X] := ExtractStr('[filein]', S2); + S2 := ReplaceText(S2, ExeStruct.InFile[X], S3); + ExeStruct.InFile[X] := ReplaceText(ExeStruct.InFile[X], '', S3); - ExeStruct^.InFile[X] := ReplaceText(ExeStruct^.InFile[X], + ExeStruct.InFile[X] := ReplaceText(ExeStruct.InFile[X], '[filein]', S3); + S2 := ExeStruct.InFile[X]; + if ContainsText(S2, '[filein]') then + continue; end else if ContainsText(S2, '') or ContainsText(S2, '[fileout]') then begin S3 := IfThen(X = 0, FILE_OUT, FILE_IN); - SetBits(ExeStruct^.Mode[X], 0, 1, 1); + SetBits(ExeStruct.Mode[X], 0, 1, 1); if ContainsText(S2, '') then - ExeStruct^.OutFile[X] := ExtractStr('', S2) + ExeStruct.OutFile[X] := ExtractStr('', S2) else - ExeStruct^.OutFile[X] := ExtractStr('[fileout]', S2); - S2 := ReplaceText(S2, ExeStruct^.OutFile[X], S3); - ExeStruct^.OutFile[X] := ReplaceText(ExeStruct^.OutFile[X], + ExeStruct.OutFile[X] := ExtractStr('[fileout]', S2); + ExeStruct.OutFile[X] := ReplaceText(ExeStruct.OutFile[X], '', S3); - ExeStruct^.OutFile[X] := ReplaceText(ExeStruct^.OutFile[X], + ExeStruct.OutFile[X] := ReplaceText(ExeStruct.OutFile[X], '[fileout]', S3); + S2 := ExeStruct.OutFile[X]; + if ContainsText(S2, '[fileout]') then + continue; end; S2 := IfThen((Pos(' ', S2) > 0) or (S2 = ''), '"' + S2 + '"', S2); - ExeStruct^.Param[X] := ExeStruct^.Param[X] + ' ' + S2; + ExeStruct.Param[X] := ExeStruct.Param[X] + ' ' + S2; end; - ExeStruct^.Param[X] := Trim(ExeStruct^.Param[X]); + ExeStruct.Param[X] := Trim(ExeStruct.Param[X]); end; - Insert(ExeStruct^, CodecExe, Length(CodecExe)); + Insert(ExeStruct, CodecExe, Length(CodecExe)); end; end; SL.Free; diff --git a/precompressor/PrecompLZ4.pas b/precompressor/PrecompLZ4.pas index a407415..5d82f21 100644 --- a/precompressor/PrecompLZ4.pas +++ b/precompressor/PrecompLZ4.pas @@ -3,9 +3,11 @@ unit PrecompLZ4; interface uses - LZ4DLL, XDeltaDLL, + LZ4DLL, + lz4, Utils, PrecompUtils, + WinAPI.Windows, System.SysUtils, System.StrUtils, System.Classes, System.Math; var @@ -163,7 +165,8 @@ procedure LZ4Scan1(Instance, Depth: Integer; Input: PByte; Funcs: PPrecompFuncs); var Buffer: PByte; - X, Y: Integer; + Pos: NativeInt; + X, Y, Z: Integer; SI: _StrInfo1; DI1, DI2: TDepthInfo; DS: TPrecompStr; @@ -173,8 +176,10 @@ begin if DS <> '' then begin X := IndexTextW(@DS[0], LZ4Codecs); - if (X < 0) or (DI1.OldSize <> SizeEx) then + if X < 0 then exit; + if DI1.OldSize = 0 then + DI1.OldSize := SizeEx; if not CodecAvailable[X] then exit; Y := Max(DI1.NewSize, L_MAXSIZE); @@ -212,6 +217,71 @@ begin end; if BoolArray(CodecEnabled, False) then exit; + Buffer := Funcs^.Allocator(Instance, L_MAXSIZE); + Pos := 0; + while Pos < Size do + begin + if CodecEnabled[LZ4_CODEC] or + (CodecEnabled[LZ4HC_CODEC] and (SOList[Instance][LZ4HC_CODEC].Count = 1)) + then + begin + Y := LZ4_decompress_safe(Input + Pos, Buffer, SizeEx - Pos, L_MAXSIZE); + if Abs(Y) > 256 then + begin + try + X := LZ4_decompress_generic(Input + Pos, Buffer, SizeEx - Pos, Abs(Y), + Integer(endOnOutputSize)); + except + X := 0; + end; + // X := Abs(X); + Y := Abs(Y); + if (Round(X * 1.4) < Y) and (X < Y) and (X > 256) then + begin + Output(Instance, Buffer, Y); + SI.Position := Pos; + SI.OldSize := X; + SI.NewSize := Y; + SI.Option := 0; + if CodecEnabled[LZ4_CODEC] then + SetBits(SI.Option, LZ4_CODEC, 0, 5) + else + SetBits(SI.Option, LZ4HC_CODEC, 0, 5); + SI.Status := TStreamStatus.None; + Funcs^.LogScan1(LZ4Codecs[GetBits(SI.Option, 0, 5)], SI.Position, + SI.OldSize, SI.NewSize); + Add(Instance, @SI, nil, nil); + Inc(Pos, 256); + continue; + end; + end; + end; + if CodecEnabled[LZ4F_CODEC] then + if PCardinal(Input + Pos)^ = $184D2204 then + begin + Y := LZ4F_decompress_safe(Input + Pos, Buffer, SizeEx - Pos, + L_MAXSIZE, @X, @Z); + if (X < Y) then + begin + Output(Instance, Buffer, Y); + SI.Position := Pos; + SI.OldSize := X; + SI.NewSize := Y; + SI.Option := 0; + SetBits(SI.Option, LZ4F_CODEC, 0, 5); + SetBits(SI.Option, Z - 4, 12, 2); + SetBits(SI.Option, 0, 14, 1); + SetBits(SI.Option, LAcceleration, 15, 7); + SI.Status := TStreamStatus.None; + Funcs^.LogScan1(LZ4Codecs[GetBits(SI.Option, 0, 5)], SI.Position, + SI.OldSize, SI.NewSize); + Add(Instance, @SI, nil, nil); + Inc(Pos, SI.OldSize); + continue; + end; + end; + Inc(Pos); + end; end; function LZ4Scan2(Instance, Depth: Integer; Input: Pointer; Size: NativeInt; @@ -271,7 +341,7 @@ begin if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; if (StreamInfo^.Status = TStreamStatus.Database) and - (GetBits(StreamInfo^.Option, 1, 31) = 0) then + (GetBits(StreamInfo^.Option, 31, 1) = 0) then begin Res1 := StreamInfo^.OldSize; Result := True; @@ -283,6 +353,8 @@ begin begin Params := 'a' + GetBits(StreamInfo^.Option, 15, 7).ToString; if not Result then + { Res1 := LZ4_compress_block(NewInput, Buffer, + StreamInfo^.NewSize, Y); } Res1 := LZ4_compress_fast(NewInput, Buffer, StreamInfo^.NewSize, Y, GetBits(StreamInfo^.Option, 15, 7)); end; @@ -314,7 +386,7 @@ begin StreamInfo^.OldSize); Funcs^.LogProcess(LZ4Codecs[GetBits(StreamInfo^.Option, 0, 5)], PChar(Params), StreamInfo^.OldSize, StreamInfo^.NewSize, Res1, Result); - if Result or (StreamInfo^.Status = TStreamStatus.Predicted) then + if Result or (StreamInfo^.Status >= TStreamStatus.Predicted) then break; end; if (Result = False) and ((StreamInfo^.Status >= TStreamStatus.Predicted) or diff --git a/precompressor/PrecompLZO.pas b/precompressor/PrecompLZO.pas index f5d8e80..99ccddc 100644 --- a/precompressor/PrecompLZO.pas +++ b/precompressor/PrecompLZO.pas @@ -3,7 +3,7 @@ unit PrecompLZO; interface uses - LZODLL, XDeltaDLL, + LZODLL, Utils, PrecompUtils, System.SysUtils, System.Classes, System.Math; @@ -14,19 +14,25 @@ var implementation const - LZOCodecs: array of PChar = ['lzo1x']; - CODEC_COUNT = 1; + LZOCodecs: array of PChar = ['lzo1x', 'lzo2a', 'lzo1c']; + CODEC_COUNT = 3; LZO1X_CODEC = 0; + LZO2A_CODEC = 1; + LZO1C_CODEC = 2; const L_WORKMEM = 524288; L_MAXSIZE = 16 * 1024 * 1024; - LZO1X_999 = 0; + LZO1X_999 = 999; + LZO2A_999 = 999; + LZO1C_999 = 999; var SOList: array of array [0 .. CODEC_COUNT - 1] of TSOList; WrkMem: array of array [0 .. L_WORKMEM - 1] of Byte; LZO1XVariant: Integer = LZO1X_999; + LZO2AVariant: Integer = LZO2A_999; + LZO1CVariant: Integer = LZO1C_999; CodecAvailable, CodecEnabled: TArray; type @@ -39,7 +45,7 @@ type var LZOSB: array of Byte = [$11, $00, $00]; -function GetLZOSI(InBuff: Pointer; InSize: Integer; OutBuff: Pointer; +function GetLZO1XSI(InBuff: Pointer; InSize: Integer; OutBuff: Pointer; OutSize: Integer; StreamInfo: PLZOSI): Boolean; const MinSize = 256; @@ -123,11 +129,25 @@ begin begin CodecEnabled[LZO1X_CODEC] := True; if Funcs^.GetParam(Command, X, 'v') = '999' then - LZO1XVariant := 0; + LZO1XVariant := 999; if Funcs^.GetParam(Command, X, 'l') <> '' then for I := Low(SOList) to High(SOList) do SOList[I][LZO1X_CODEC].Update ([StrToInt(Funcs^.GetParam(Command, X, 'l'))], True); + end + else if (CompareText(S, LZOCodecs[LZO2A_CODEC]) = 0) and LZODLL.DLLLoaded + then + begin + CodecEnabled[LZO2A_CODEC] := True; + if Funcs^.GetParam(Command, X, 'v') = '999' then + LZO2AVariant := 999; + end + else if (CompareText(S, LZOCodecs[LZO1C_CODEC]) = 0) and LZODLL.DLLLoaded + then + begin + CodecEnabled[LZO1C_CODEC] := True; + if Funcs^.GetParam(Command, X, 'v') = '999' then + LZO1CVariant := 999; end; Inc(X); end; @@ -135,9 +155,14 @@ begin for I := 1 to 9 do Insert(I, Options, Length(Options)); for X := Low(SOList) to High(SOList) do - for Y := Low(SOList[X]) to High(SOList[X]) do - if SOList[X, Y].Count = 0 then - SOList[X, Y].Update(Options); + if SOList[X, LZO1X_CODEC].Count = 0 then + SOList[X, LZO1X_CODEC].Update(Options); + for X := Low(SOList) to High(SOList) do + if SOList[X, LZO2A_CODEC].Count = 0 then + SOList[X, LZO2A_CODEC].Update([1]); + for X := Low(SOList) to High(SOList) do + if SOList[X, LZO1C_CODEC].Count = 0 then + SOList[X, LZO1C_CODEC].Update([1]); end; procedure LZOFree(Funcs: PPrecompFuncs); @@ -164,11 +189,29 @@ begin if (CompareText(S, LZOCodecs[LZO1X_CODEC]) = 0) and LZODLL.DLLLoaded then begin SetBits(Option^, LZO1X_CODEC, 0, 5); - SetBits(Option^, LZO1XVariant, 12, 5); + SetBits(Option^, LZO1XVariant, 12, 12); if Funcs^.GetParam(Command, I, 'l') <> '' then SetBits(Option^, StrToInt(Funcs^.GetParam(Command, I, 'l')), 5, 7); if Funcs^.GetParam(Command, I, 'v') = '999' then - SetBits(Option^, 0, 12, 5); + SetBits(Option^, 999, 12, 12); + Result := True; + end + else if (CompareText(S, LZOCodecs[LZO2A_CODEC]) = 0) and LZODLL.DLLLoaded + then + begin + SetBits(Option^, LZO2A_CODEC, 0, 5); + SetBits(Option^, LZO2AVariant, 12, 12); + if Funcs^.GetParam(Command, I, 'v') = '999' then + SetBits(Option^, 999, 12, 12); + Result := True; + end + else if (CompareText(S, LZOCodecs[LZO1C_CODEC]) = 0) and LZODLL.DLLLoaded + then + begin + SetBits(Option^, LZO1C_CODEC, 0, 5); + SetBits(Option^, LZO1CVariant, 12, 12); + if Funcs^.GetParam(Command, I, 'v') = '999' then + SetBits(Option^, 999, 12, 12); Result := True; end; Inc(I); @@ -203,6 +246,12 @@ begin LZO1X_CODEC: if not lzo1x_decompress_safe(Input, DI1.OldSize, Buffer, @Res) = 0 then Res := 0; + LZO2A_CODEC: + if not lzo2a_decompress_safe(Input, DI1.OldSize, Buffer, @Res) = 0 then + Res := 0; + LZO1C_CODEC: + if not lzo1c_decompress_safe(Input, DI1.OldSize, Buffer, @Res) = 0 then + Res := 0; end; if (Res > DI1.OldSize) then begin @@ -212,6 +261,14 @@ begin SI.NewSize := Res; SI.Option := 0; SetBits(SI.Option, X, 0, 5); + case X of + LZO1X_CODEC: + SetBits(SI.Option, LZO1XVariant, 12, 12); + LZO2A_CODEC: + SetBits(SI.Option, LZO2AVariant, 12, 12); + LZO1C_CODEC: + SetBits(SI.Option, LZO1CVariant, 12, 12); + end; if System.Pos(SPrecompSep2, DI1.Codec) > 0 then SI.Status := TStreamStatus.Predicted else @@ -232,13 +289,15 @@ begin Pos := 0; while Pos < Size do begin - if GetLZOSI(Input + Pos, SizeEx - Pos, Buffer, L_MAXSIZE, @LZOSI) then + if GetLZO1XSI(Input + Pos, SizeEx - Pos, Buffer, L_MAXSIZE, @LZOSI) then begin Output(Instance, Buffer, LZOSI.DSize); SI.Position := Pos; SI.OldSize := LZOSI.CSize; SI.NewSize := LZOSI.DSize; SI.Option := 0; + SetBits(SI.Option, LZO1X_CODEC, 0, 5); + SetBits(SI.Option, LZO1XVariant, 12, 12); SI.Status := TStreamStatus.None; Funcs^.LogScan1(LZOCodecs[GetBits(SI.Option, 0, 5)], SI.Position, SI.OldSize, SI.NewSize); @@ -269,6 +328,14 @@ begin if not lzo1x_decompress_safe(Input, StreamInfo^.OldSize, Buffer, @Res) = 0 then Res := 0; + LZO2A_CODEC: + if not lzo2a_decompress_safe(Input, StreamInfo^.OldSize, Buffer, @Res) = 0 + then + Res := 0; + LZO1C_CODEC: + if not lzo1c_decompress_safe(Input, StreamInfo^.OldSize, Buffer, @Res) = 0 + then + Res := 0; end; if Res > StreamInfo^.OldSize then begin @@ -303,7 +370,7 @@ begin if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; if (StreamInfo^.Status = TStreamStatus.Database) and - (GetBits(StreamInfo^.Option, 1, 31) = 0) then + (GetBits(StreamInfo^.Option, 31, 1) = 0) then begin Res1 := StreamInfo^.OldSize; Result := True; @@ -313,19 +380,38 @@ begin Res1 := StreamInfo^.NewSize; case X of LZO1X_CODEC: - case GetBits(StreamInfo^.Option, 12, 5) of + case GetBits(StreamInfo^.Option, 12, 12) of LZO1X_999: begin Params := 'l' + I.ToString + ':' + 'v' + - GetBits(StreamInfo^.Option, 12, 5).ToString; + GetBits(StreamInfo^.Option, 12, 12).ToString; if not Result then if not lzo1x_999_compress_level(NewInput, StreamInfo^.NewSize, Buffer, @Res1, @WrkMem[Instance, 0], nil, 0, nil, I) = 0 then Res1 := 0; end; - { if not lzo1x_1_compress(NewInput, StreamInfo^.NewSize, Buffer, - @Res1, @WrkMem[Instance, 0]) = 0 then - Res1 := 0; } + end; + LZO2A_CODEC: + case GetBits(StreamInfo^.Option, 12, 12) of + LZO2A_999: + begin + Params := 'v' + GetBits(StreamInfo^.Option, 12, 12).ToString; + if not Result then + if not lzo2a_999_compress(NewInput, StreamInfo^.NewSize, Buffer, + @Res1, @WrkMem[Instance, 0]) = 0 then + Res1 := 0; + end; + end; + LZO1C_CODEC: + case GetBits(StreamInfo^.Option, 12, 12) of + LZO1C_999: + begin + Params := 'v' + GetBits(StreamInfo^.Option, 12, 12).ToString; + if not Result then + if not lzo1c_999_compress(NewInput, StreamInfo^.NewSize, Buffer, + @Res1, @WrkMem[Instance, 0]) = 0 then + Res1 := 0; + end; end; end; if not Result then @@ -333,25 +419,25 @@ begin StreamInfo^.OldSize); Funcs^.LogProcess(LZOCodecs[GetBits(StreamInfo^.Option, 0, 5)], PChar(Params), StreamInfo^.OldSize, StreamInfo^.NewSize, Res1, Result); - if Result or (StreamInfo^.Status = TStreamStatus.Predicted) then + if Result or (StreamInfo^.Status >= TStreamStatus.Predicted) then break; end; - if (Result = False) and ((StreamInfo^.Status >= TStreamStatus.Predicted) or + { if (Result = False) and ((StreamInfo^.Status >= TStreamStatus.Predicted) or (SOList[Instance][X].Count = 1)) and (DIFF_TOLERANCE > 0) then - begin + begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); Res2 := PrecompEncodePatch(OldInput, StreamInfo^.OldSize, Buffer, Res1, - Buffer + Res1, Max(StreamInfo^.OldSize, Res1)); + Buffer + Res1, Max(StreamInfo^.OldSize, Res1)); Funcs^.LogPatch1(StreamInfo^.OldSize, Res1, Res2, - Funcs^.AcceptPatch(StreamInfo^.OldSize, Res1, Res2)); + Funcs^.AcceptPatch(StreamInfo^.OldSize, Res1, Res2)); if Funcs^.AcceptPatch(StreamInfo^.OldSize, Res1, Res2) then begin - Output(Instance, Buffer + Res1, Res2); - SetBits(StreamInfo^.Option, 1, 31, 1); - SOList[Instance][X].Add(I); - Result := True; + Output(Instance, Buffer + Res1, Res2); + SetBits(StreamInfo^.Option, 1, 31, 1); + SOList[Instance][X].Add(I); + Result := True; end; - end; + end; } if Result then begin SetBits(StreamInfo^.Option, I, 5, 7); @@ -377,17 +463,37 @@ begin Res1 := StreamInfo.NewSize; case X of LZO1X_CODEC: - case GetBits(StreamInfo.Option, 12, 5) of + case GetBits(StreamInfo.Option, 12, 12) of LZO1X_999: begin Params := 'l' + GetBits(StreamInfo.Option, 5, 7).ToString + ':' + - 'v' + GetBits(StreamInfo.Option, 12, 5).ToString; + 'v' + GetBits(StreamInfo.Option, 12, 12).ToString; if not lzo1x_999_compress_level(Input, StreamInfo.NewSize, Buffer, @Res1, @WrkMem[Instance, 0], nil, 0, nil, GetBits(StreamInfo.Option, 5, 7)) = 0 then Res1 := 0; end; end; + LZO2A_CODEC: + case GetBits(StreamInfo.Option, 12, 12) of + LZO2A_999: + begin + Params := 'v' + GetBits(StreamInfo.Option, 12, 12).ToString; + if not lzo2a_999_compress(Input, StreamInfo.NewSize, Buffer, @Res1, + @WrkMem[Instance, 0]) = 0 then + Res1 := 0; + end; + end; + LZO1C_CODEC: + case GetBits(StreamInfo.Option, 12, 12) of + LZO1C_999: + begin + Params := 'v' + GetBits(StreamInfo.Option, 12, 12).ToString; + if not lzo2a_999_compress(Input, StreamInfo.NewSize, Buffer, @Res1, + @WrkMem[Instance, 0]) = 0 then + Res1 := 0; + end; + end; end; Funcs^.LogRestore(LZOCodecs[GetBits(StreamInfo.Option, 0, 5)], PChar(Params), StreamInfo.OldSize, StreamInfo.NewSize, Res1, True); diff --git a/precompressor/PrecompMain.pas b/precompressor/PrecompMain.pas index c02530c..1aa9f42 100644 --- a/precompressor/PrecompMain.pas +++ b/precompressor/PrecompMain.pas @@ -104,7 +104,7 @@ var DBFile: String = ''; ExtDir: String = ''; UseDB: Boolean = False; - StoreDD: Boolean = False; + StoreDD: Integer = -2; VERBOSE: Boolean = False; EXTRACT: Boolean = False; DupSysMem: Int64 = 0; @@ -183,26 +183,31 @@ begin S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); - Options.Depth := EnsureRange(Succ(ArgParse.AsInteger('-d')), 1, 10); + Options.Depth := EnsureRange(Succ(ArgParse.AsInteger('-d', 0, 0)), 1, 10); Options.LowMem := ArgParse.AsBoolean('-lm'); - UseDB := ArgParse.AsBoolean('--dbase'); + UseDB := ArgParse.AsBoolean('-db') or ArgParse.AsBoolean('--dbase'); Options.DBaseFile := ArgParse.AsString('--dbase='); if Options.DBaseFile <> '' then UseDB := True; - StoreDD := ArgParse.AsBoolean('--dedup'); + StoreDD := -2; + if ArgParse.AsBoolean('-dd') or ArgParse.AsBoolean('--dedup') then + StoreDD := -1; + if FileExists(ExtractFilePath(Utils.GetModuleName) + 'srep.exe') then + StoreDD := ArgParse.AsInteger('--dedup=', 0, StoreDD); S := ArgParse.AsString('--diff=', 0, '5p'); S := ReplaceText(S, 'p', '%'); DIFF_TOLERANCE := Max(0.00, ExpParse.Evaluate(S)); - VERBOSE := ArgParse.AsBoolean('--verbose'); + VERBOSE := ArgParse.AsBoolean('-v') or ArgParse.AsBoolean('--verbose'); Options.ExtractDir := ArgParse.AsString('--extract='); if Options.ExtractDir <> '' then EXTRACT := DirectoryExists(Options.ExtractDir); - Options.DoCompress := ArgParse.AsBoolean('--compress'); + Options.DoCompress := ArgParse.AsBoolean('--compress') and + FLZMA2DLL.DLLLoaded; S := ArgParse.AsString('--compress='); S := ReplaceText(S, SPrecompSep3, SPrecompSep2); Options.CompressCfg := S; if Options.CompressCfg <> '' then - Options.DoCompress := True; + Options.DoCompress := FLZMA2DLL.DLLLoaded; finally ArgParse.Free; ExpParse.Free; @@ -1191,7 +1196,7 @@ begin for I := Low(DBInfo) to High(DBInfo) do DBCount[I] := 0; end; - if StoreDD then + if StoreDD > -2 then begin SetLength(DDInfo, $10000); SetLength(DDCount1, $10000); @@ -1409,6 +1414,7 @@ var BlockSize: Int64; UI32: UInt32; I, J, K, X: Integer; + S: String; W: Word; I64: Int64; LastStream, LastPos: Int64; @@ -1420,8 +1426,11 @@ var begin if (Depth = 0) then begin - if StoreDD then - TempOutput := TPrecompVMStream.Create + if StoreDD > -2 then + TempOutput := TBufferedStream.Create + (TFileStream.Create + (LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '-dd.tmp')), fmCreate or fmShareDenyNone), False, 4194304) else TempOutput := Output; end @@ -1525,7 +1534,7 @@ begin begin Inc(StreamCount); DupBool := False; - if (Depth = 0) and StoreDD then + if (Depth = 0) and (StoreDD > -2) then DupBool := not FindOrAddDD(StreamInfo, @DupIdx2, @DupCount); if DupBool then begin @@ -1580,7 +1589,7 @@ begin while J >= 0 do begin DupBool := False; - if (Depth = 0) and StoreDD then + if (Depth = 0) and (StoreDD > -2) then DupBool := FindDD(StreamInfo, @DupIdx2, @DupCount); if (DupBool = False) or (DupIdx1 = DupIdx2) then begin @@ -1663,9 +1672,9 @@ begin with WorkStream[0] do begin Position := 0; - for W := 0 to $10000 - 1 do + for W := Low(DBInfo) to High(DBInfo) do begin - J := DBCount[I]; + J := DBCount[W]; if J > 0 then begin WriteBuffer(W, W.Size); @@ -1682,7 +1691,7 @@ begin Free; end; end; - if StoreDD then + if StoreDD > -2 then begin with WorkStream[0] do begin @@ -1703,9 +1712,37 @@ begin end; Output.WriteBuffer(UI32, UI32.Size); Output.WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); - Output.CopyFrom(TempOutput, 0); + try + EncFree; + finally + end; + S := TFileStream(TBufferedStream(TempOutput).Instance).Filename; + TBufferedStream(TempOutput).Flush; + if StoreDD >= 0 then + begin + with TProcessStream.Create(ExtractFilePath(Utils.GetModuleName) + + 'srep.exe', '-m' + StoreDD.ToString + 'f ' + S + ' -', GetCurrentDir, + nil, Output) do + try + if Execute then + begin + Wait; + Done; + end; + finally + Free; + end; + end + else + Output.CopyFrom(TempOutput, 0); TempOutput.Free; - end; + DeleteFile(S); + end + else + try + EncFree; + finally + end; end; end; @@ -1776,7 +1813,7 @@ procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; begin with ComVars2[CurDepth[Instance]] do DecOutput[Instance].WriteBuffer(Buffer^, Size); - if StoreDD and (CurDepth[Instance] = 0) then + if (StoreDD > -2) and (CurDepth[Instance] = 0) then if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) then DataMgr.Write(DDIndex1, Buffer, Size); end; @@ -1821,7 +1858,7 @@ begin end else begin - if StoreDD and (Depth = 0) then + if (StoreDD > -2) and (Depth = 0) then begin Inc(DDIndex1); if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) @@ -1886,7 +1923,7 @@ begin end else begin - if StoreDD and (Depth = 0) then + if (StoreDD > -2) and (Depth = 0) then if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) then Inc(DDIndex2); @@ -2039,11 +2076,12 @@ var CurrPos: Int64; UI32: UInt32; I, J: Integer; + LStream: TProcessStream; begin if Depth = 0 then begin UI32 := 0; - if StoreDD then + if (StoreDD > -2) then begin Input.ReadBuffer(UI32, UI32.Size); SetLength(DDList2, UI32); @@ -2057,7 +2095,16 @@ begin end; with ComVars2[Depth] do begin - DecInput[Index] := Input; + if (Depth = 0) and (StoreDD >= 0) then + begin + LStream := TProcessStream.Create(ExtractFilePath(Utils.GetModuleName) + + 'srep.exe', '-d -s - -', GetCurrentDir, Input, nil); + if not LStream.Execute then + raise EReadError.CreateRes(@SReadError); + DecInput[Index] := TBufferedStream.Create(LStream, True, 4194304); + end + else + DecInput[Index] := Input; DecOutput[Index] := Output; DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); while StreamCount[Index]^ >= 0 do @@ -2137,7 +2184,7 @@ begin if IsErrored(Tasks) then for I := Low(Tasks) to High(Tasks) do Tasks[I].RaiseLastError; - if StoreDD and (Depth = 0) then + if (StoreDD > -2) and (Depth = 0) then begin Inc(DDIndex1); if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) @@ -2168,6 +2215,15 @@ begin CopyStreamEx(DecInput[Index], DecOutput[Index], UI32); DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); end; + if (Depth = 0) and (StoreDD >= 0) then + begin + with LStream do + begin + Wait; + Done; + end; + DecInput[Index].Free; + end; end; end; @@ -2294,7 +2350,7 @@ begin if Options.DoCompress then LOutput.Free; try - EncFree; + // EncFree; finally Stopwatch.Stop; end; diff --git a/precompressor/PrecompMedia.pas b/precompressor/PrecompMedia.pas index 81a71f9..032de67 100644 --- a/precompressor/PrecompMedia.pas +++ b/precompressor/PrecompMedia.pas @@ -5,7 +5,7 @@ unit PrecompMedia; interface uses - BrunsliDLL, FLACDLL, PackJPGDLL, JoJpegDLL, XDeltaDLL, + BrunsliDLL, FLACDLL, PackJPGDLL, JoJpegDLL, Utils, PrecompUtils, System.SysUtils, System.Classes, System.Math; diff --git a/precompressor/PrecompOodle.pas b/precompressor/PrecompOodle.pas index eae05c0..4706056 100644 --- a/precompressor/PrecompOodle.pas +++ b/precompressor/PrecompOodle.pas @@ -3,26 +3,11 @@ unit PrecompOodle; interface uses - OodleDLL, XDeltaDLL, + OodleDLL, Utils, PrecompUtils, System.SysUtils, System.Classes, System.Types, System.Math; -{ 8C 07 - 0:LZH - 8C 00 - 1:LZHLW - 8C 01 - 2:LZNIB - CC 07 - 3:None - 8C 02 - 4:LZB16 - 8C 03 - 5:LZBLW - 8C 04 - 6:LZA - 8C 05 - 7:LZNA - 8C 06 - 8:Kraken - 8C 0A - 9:Mermaid - 8C 0B - 10:BitKnit - 8C 0A - 11:Selkie - 8C 0A - 12:Hydra - 8C 0C - 13:Leviathan } - var Codec: TPrecompressor; @@ -40,13 +25,13 @@ const LEVIATHAN_CODEC = 5; const - O_COUNT = 0; + O_LENGTH = 32; O_TRADEOFF = 256; O_MAXSIZE = 16 * 1024 * 1024; var SOList: array of array [0 .. CODEC_COUNT - 1] of TSOList; - OCount: Integer = O_COUNT; + OLength: Integer = O_LENGTH; OTradeOff: Integer = O_TRADEOFF; CodecAvailable, CodecEnabled: TArray; @@ -247,135 +232,36 @@ begin Result := A; end; -function CustomLZ_Decompress0(src, dst: PByte; srcSize, dstCapacity: Integer; +function CustomLZ_Decompress(src, dst: PByte; srcSize, dstCapacity: Integer; var Res: Integer): Boolean; - -type - T3Res = array [0 .. 2] of Integer; - - procedure AddRes(const I: Integer; var Res: T3Res); - begin - Res[0] := Res[1]; - Res[1] := Res[2]; - Res[2] := I; - end; - const - MinSize = 64; BlkSize = 262144; - Range = 262144; - - function ValidSize(Res: T3Res): Boolean; - const - ThresSize = 32; - begin - Result := (Res[0] > 0) and (Res[0] < Res[1]) and - InRange(Res[0], Res[0], Res[2] + 32); - end; - var - LBuffer: array [0 .. BlkSize - 1] of Byte; - I, J, W, X, Y, Z: Integer; - LR1, LR2: T3Res; + W, X, Y, Z: Integer; begin Result := False; - Y := Max(LocalLZ_Decompress(src, dst, srcSize, dstCapacity, 0, Z), - LocalLZ_Decompress(src, dst, srcSize, dstCapacity, 1, Z)); - if Y > MinSize then + Y := 0; + X := dstCapacity; + X := Min(Max(LocalLZ_Decompress(src, dst, srcSize, X, 0, Y), + LocalLZ_Decompress(src, dst, srcSize, X, 1, Z)), Pred(X)); + W := X; + while (Y = 0) and (X > dstCapacity - BlkSize) and (W - X < OLength) do begin - W := IfThen(Y mod BlkSize = 0, Pred(Y div BlkSize), Y div BlkSize) - * BlkSize; - Move((dst + W)^, LBuffer[0], Y - W); + X := Min(Max(LocalLZ_Decompress(src, dst, srcSize, X, 0, Y), + LocalLZ_Decompress(src, dst, srcSize, X, 1, Y)), Pred(X)); end; - if (Y = Z) and (Y = dstCapacity) then + X := Min(Succ(W), dstCapacity); + while (Z = 0) and (X < Min(W + OLength, dstCapacity)) do + begin + LocalLZ_Decompress(src, dst, srcSize, X, 0, Z); + Inc(X); + end; + Y := Max(Y, Z); + if (Y > 0) then begin Res := Y; - I := Max(LocalLZ_Decompress(src, dst, srcSize, dstCapacity - 1, 0, Z), - LocalLZ_Decompress(src, dst, srcSize, dstCapacity - 1, 1, Z)); - if (Res <> I) and (Res <> Pred(I)) then - begin - Move(LBuffer[0], (dst + W)^, Res - W); - Result := True; - exit; - end; + Result := True; end; - FillChar(LR1, SizeOf(T3Res), 0); - FillChar(LR2, SizeOf(T3Res), 0); - I := Y; - J := Min(dstCapacity, Y + Range); - while I < J do - begin - Y := Max(LocalLZ_Decompress(src, dst, srcSize, I, 0, Z), - LocalLZ_Decompress(src, dst, srcSize, I, 1, Z)); - AddRes(Y, LR1); - AddRes(Z, LR2); - if (LR1[1] = LR2[1]) and ValidSize(LR1) then - begin - Res := LR1[1]; - Move(LBuffer[0], (dst + W)^, Res - W); - Result := True; - break; - end; - if Y > MinSize then - begin - W := IfThen(Y mod BlkSize = 0, Pred(Y div BlkSize), Y div BlkSize) - * BlkSize; - Move((dst + W)^, LBuffer[0], Y - W); - end; - Inc(I); - end; -end; - -function CustomLZ_DecompressN(src, dst: PByte; srcSize, dstCapacity: Integer; - var Res: TIntegerDynArray): Boolean; - -const - BlkSize = 262144; - UpLen = 128; - DownLen = 16; -var - I, J, X, Y, Z: Integer; - Sizes: array [0 .. UpLen + DownLen - 1] of Integer; -begin - SetLength(Res, 0); - Y := Max(LocalLZ_Decompress(src, dst, srcSize, dstCapacity, 0, Z), - LocalLZ_Decompress(src, dst, srcSize, dstCapacity, 1, Z)); - for I := Low(Sizes) to High(Sizes) do - Sizes[I] := -1; - J := Min(dstCapacity, Y + UpLen); - I := Max(IfThen(dstCapacity mod BlkSize = 0, Pred(dstCapacity div BlkSize), - dstCapacity div BlkSize) * BlkSize, Y - DownLen); - X := J - I; - while (J > I) do - begin - Y := Max(LocalLZ_Decompress(src, dst, srcSize, J, 0, Z), - LocalLZ_Decompress(src, dst, srcSize, J, 1, Z)); - Sizes[Length(Sizes) - (J - I)] := Z; - Dec(J); - end; - for I := Low(Sizes) to High(Sizes) do - begin - X := Sizes[I]; - for J := Low(Sizes) to High(Sizes) do - begin - Y := Sizes[J]; - if I <> J then - if X = Y then - begin - Sizes[I] := -1; - Sizes[J] := -1; - end; - end; - end; - for I := Low(Sizes) to High(Sizes) do - if Sizes[I] > srcSize then - if OodleLZ_Decompress(src, srcSize, dst, Sizes[I]) = Sizes[I] then - begin - Insert(Sizes[I], Res, Length(Res)); - if Length(Res) >= OCount then - break; - end; - Result := Length(Res) > 0; end; function GetOodleUS(Instance: Integer; Input: PByte; Pos: NativeInt; @@ -386,25 +272,29 @@ const var Buffer: PByte; B: Boolean; - I: Integer; - ResultN: TIntegerDynArray; SI: _StrInfo1; begin Result := 0; - { if StreamInfo^.Codec = 3 then - exit; } - // StreamInfo^.DSize:=$8001; - Buffer := Funcs^.Allocator(Instance, StreamInfo^.DSize); - if OCount <= 0 then - B := CustomLZ_Decompress0(Input + Pos, Buffer, StreamInfo^.CSize, - StreamInfo^.DSize, Result) + case StreamInfo^.Codec of + 1: + if (CodecEnabled[KRAKEN_CODEC] = False) and + (CodecEnabled[HYDRA_CODEC] = False) then + exit; + 2: + if (CodecEnabled[MERMAID_CODEC] = False) and + (CodecEnabled[SELKIE_CODEC] = False) and + (CodecEnabled[HYDRA_CODEC] = False) then + exit; + 3: + if (CodecEnabled[LEVIATHAN_CODEC] = False) and + (CodecEnabled[HYDRA_CODEC] = False) then + exit; else - begin - B := CustomLZ_DecompressN(Input + Pos, Buffer, StreamInfo^.CSize, - StreamInfo^.DSize, ResultN); - if B then - Result := ResultN[0]; + exit; end; + Buffer := Funcs^.Allocator(Instance, StreamInfo^.DSize); + B := CustomLZ_Decompress(Input + Pos, Buffer, StreamInfo^.CSize, + StreamInfo^.DSize, Result); If B then if (Result > MinSize) and (Result > StreamInfo^.CSize) then begin @@ -415,37 +305,25 @@ begin SetBits(SI.Option, OTradeOff, 13, 11); case StreamInfo^.Codec of 1: - SetBits(SI.Option, KRAKEN_CODEC, 0, 5); + if CodecEnabled[KRAKEN_CODEC] then + SetBits(SI.Option, KRAKEN_CODEC, 0, 5); 2: if CodecEnabled[MERMAID_CODEC] then SetBits(SI.Option, MERMAID_CODEC, 0, 5) - else + else if CodecEnabled[SELKIE_CODEC] then SetBits(SI.Option, SELKIE_CODEC, 0, 5); 3: - SetBits(SI.Option, LEVIATHAN_CODEC, 0, 5); + if CodecEnabled[LEVIATHAN_CODEC] then + SetBits(SI.Option, LEVIATHAN_CODEC, 0, 5); end; if CodecEnabled[HYDRA_CODEC] then SetBits(SI.Option, HYDRA_CODEC, 0, 5); SetBits(SI.Option, Integer(StreamInfo^.HasCRC), 12, 1); SI.Status := TStreamStatus.None; - if OCount <= 0 then - begin - SI.NewSize := Result; - Funcs^.LogScan1(OodleCodecs[GetBits(SI.Option, 0, 5)], SI.Position, - SI.OldSize, SI.NewSize); - Add(Instance, @SI, nil, nil); - end - else - begin - if Length(ResultN) > 0 then - for I := Low(ResultN) to High(ResultN) do - begin - SI.NewSize := ResultN[I]; - Funcs^.LogScan1(OodleCodecs[GetBits(SI.Option, 0, 5)], SI.Position, - SI.OldSize, SI.NewSize); - Add(Instance, @SI, nil, nil); - end; - end; + SI.NewSize := Result; + Funcs^.LogScan1(OodleCodecs[GetBits(SI.Option, 0, 5)], SI.Position, + SI.OldSize, SI.NewSize); + Add(Instance, @SI, nil, nil); end; end; @@ -502,7 +380,7 @@ begin SOList[I][Y].Update ([StrToInt(Funcs^.GetParam(Command, X, 'l'))], True); if Funcs^.GetParam(Command, X, 'n') <> '' then - OCount := StrToInt(Funcs^.GetParam(Command, X, 'n')); + OLength := StrToInt(Funcs^.GetParam(Command, X, 'n')); if Funcs^.GetParam(Command, X, 't') <> '' then OTradeOff := StrToInt(Funcs^.GetParam(Command, X, 't')); end; @@ -573,7 +451,11 @@ begin if DS <> '' then begin X := IndexTextW(@DS[0], OodleCodecs); - if (X < 0) or (DI1.OldSize <> SizeEx) then + if X < 0 then + exit; + if DI1.OldSize = 0 then + DI1.OldSize := SizeEx; + if not CodecAvailable[X] then exit; if not CodecAvailable[X] then exit; @@ -589,7 +471,7 @@ begin begin if DI1.NewSize <= 0 then begin - if not CustomLZ_Decompress0(Input, Buffer, DI1.OldSize, Res, Res) + if not CustomLZ_Decompress(Input, Buffer, DI1.OldSize, Res, Res) then Res := 0; end @@ -678,7 +560,7 @@ begin then begin Buffer := Funcs^.Allocator(Instance, OodleSI.DSize); - if CustomLZ_Decompress0(Input, Buffer, StreamInfo^.OldSize, + if CustomLZ_Decompress(Input, Buffer, StreamInfo^.OldSize, OodleSI.DSize, Res) then begin Output(Instance, Buffer, Res); @@ -716,7 +598,7 @@ begin if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; if (StreamInfo^.Status = TStreamStatus.Database) and - (GetBits(StreamInfo^.Option, 1, 31) = 0) then + (GetBits(StreamInfo^.Option, 31, 1) = 0) then begin Res1 := StreamInfo^.OldSize; Result := True; @@ -726,6 +608,7 @@ begin SizeOf(TOodleLZ_CompressOptions)); COptions.sendQuantumCRCs := GetBits(StreamInfo^.Option, 12, 1) = 1; COptions.spaceSpeedTradeoffBytes := GetBits(StreamInfo^.Option, 13, 11); + // COptions.dictionarySize := 262144; Params := 'l' + I.ToString + ':' + 'c' + GetBits(StreamInfo^.Option, 12, 1) .ToString + ':' + 't' + GetBits(StreamInfo^.Option, 13, 11).ToString; if not Result then @@ -739,22 +622,22 @@ begin if Result or (StreamInfo^.Status = TStreamStatus.Predicted) then break; end; - if (Result = False) and ((StreamInfo^.Status >= TStreamStatus.Predicted) or + { if (Result = False) and ((StreamInfo^.Status >= TStreamStatus.Predicted) or (SOList[Instance][X].Count = 1)) and (DIFF_TOLERANCE > 0) then - begin + begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); Res2 := PrecompEncodePatch(OldInput, StreamInfo^.OldSize, Buffer, Res1, - Buffer + Res1, Max(StreamInfo^.OldSize, Res1)); + Buffer + Res1, Max(StreamInfo^.OldSize, Res1)); Funcs^.LogPatch1(StreamInfo^.OldSize, Res1, Res2, - Funcs^.AcceptPatch(StreamInfo^.OldSize, Res1, Res2)); + Funcs^.AcceptPatch(StreamInfo^.OldSize, Res1, Res2)); if Funcs^.AcceptPatch(StreamInfo^.OldSize, Res1, Res2) then begin - Output(Instance, Buffer + Res1, Res2); - SetBits(StreamInfo^.Option, 1, 31, 1); - SOList[Instance][X].Add(I); - Result := True; + Output(Instance, Buffer + Res1, Res2); + SetBits(StreamInfo^.Option, 1, 31, 1); + SOList[Instance][X].Add(I); + Result := True; end; - end; + end; } if Result then begin SetBits(StreamInfo^.Option, I, 5, 7); diff --git a/precompressor/PrecompUtils.pas b/precompressor/PrecompUtils.pas index 82460bd..d1eb443 100644 --- a/precompressor/PrecompUtils.pas +++ b/precompressor/PrecompUtils.pas @@ -16,7 +16,7 @@ resourcestring SPrecompSep2 = ':'; SPrecompSep3 = ','; SPrecompSep4 = '/'; - SPrecompSep5 = '/'; + SPrecompSep5 = '\'; const SuccessStatus = 4; @@ -276,7 +276,7 @@ type procedure SetSize(const NewSize: Int64); override; procedure SetSize(NewSize: Longint); override; private - FInitialised: Boolean; + FInitialised, FDone: Boolean; FStream: TStream; FFilename: String; procedure Initialise; @@ -286,6 +286,8 @@ type function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; + procedure Done; + property FileName: String read FFilename; end; PResData = ^TResData; @@ -449,13 +451,15 @@ constructor TPrecompVMStream.Create; begin inherited Create; FInitialised := False; + FDone := False; end; destructor TPrecompVMStream.Destroy; begin if FInitialised then begin - FStream.Free; + if not FDone then + FStream.Free; DeleteFile(FFilename); end; inherited Destroy; @@ -521,6 +525,12 @@ begin Result := 0; end; +procedure TPrecompVMStream.Done; +begin + FStream.Free; + FDone := True; +end; + function PrecompGetCodec(Cmd: PChar; Index: Integer; WithParams: Boolean) : TPrecompStr; var @@ -567,14 +577,16 @@ begin List2 := DecodeStr(List1[Index], SPrecompSep2); if Param = '' then begin - if Length(List1) > 1 then - begin - S := ''; - if not ResourceExists(List2[I]) then - S := S + List2[I] + SPrecompSep2; - if Length(S) > 0 then - S := S.Remove(Pred(Length(S))); - end; + S := ''; + for I := Succ(Low(List2)) to High(List2) do + if ResourceExists(List2[I]) = False then + begin + if S <> '' then + S := S + SPrecompSep2; + S := S + List2[I]; + end; + if S = '' then + S := ' '; end else begin @@ -705,8 +717,9 @@ var S: String; begin Result := 0; - case IndexText(Codec, ['zlib', 'lz4', 'lz4hc', 'lzo1c', 'lzo1x', 'lzo2a', - 'zstd', 'lzna', 'kraken', 'mermaid', 'selkie', 'hydra', 'leviathan']) of + case IndexText(Codec, ['zlib', 'lz4', 'lz4hc', 'lz4f', 'lzo1c', 'lzo1x', + 'lzo2a', 'zstd', 'lzna', 'kraken', 'mermaid', 'selkie', 'hydra', + 'leviathan']) of 0: if ZLibDLL.DLLLoaded then begin @@ -729,10 +742,13 @@ begin 1, 2: if LZ4DLL.DLLLoaded then Result := LZ4_decompress_safe(InBuff, OutBuff, InSize, OutSize); - 6: + 3: + if LZ4DLL.DLLLoaded then + Result := LZ4F_decompress_safe(InBuff, OutBuff, InSize, OutSize); + 7: if ZSTDDLL.DLLLoaded then Result := ZSTD_decompress(OutBuff, OutSize, InBuff, InSize); - 7 .. 12: + 8 .. 13: if OodleDLL.DLLLoaded then Result := OodleLZ_Decompress(InBuff, InSize, OutBuff, OutSize); end; @@ -855,6 +871,8 @@ var Res: NativeUInt; begin Result := 0; + if not XDeltaDLL.DLLLoaded then + exit; if xd3_encode(OldBuff, OldSize, NewBuff, NewSize, PatchBuff, @Res, PatchSize, Integer(XD3_NOCOMPRESS)) = 0 then Result := Res; @@ -868,6 +886,8 @@ var Res: NativeUInt; begin Result := 0; + if not XDeltaDLL.DLLLoaded then + exit; if xd3_decode(PatchBuff, PatchSize, OldBuff, OldSize, NewBuff, @Res, NewSize, Integer(XD3_NOCOMPRESS)) = 0 then Result := Res; @@ -1234,5 +1254,7 @@ EncodeSICmp := TEncodeSIComparer.Create; FutureSICmp := TFutureSIComparer.Create; StockMethods := TStringList.Create; ExternalMethods := TStringList.Create; +if not XDeltaDLL.DLLLoaded then + DIFF_TOLERANCE := 0; end. diff --git a/precompressor/PrecompZLib.pas b/precompressor/PrecompZLib.pas index 5ae50ee..a844c23 100644 --- a/precompressor/PrecompZLib.pas +++ b/precompressor/PrecompZLib.pas @@ -530,7 +530,7 @@ begin Res := inflate(ZStream^, Z_BLOCK); if not(Res in [Z_OK, Z_STREAM_END]) then begin - if (LastIn >= Z_MINSIZE) then + if (Res <> Z_DATA_ERROR) and (LastIn >= Z_MINSIZE) then Res := Z_STREAM_END; break; end; diff --git a/precompressor/PrecompZSTD.pas b/precompressor/PrecompZSTD.pas index bb60918..fb3dfad 100644 --- a/precompressor/PrecompZSTD.pas +++ b/precompressor/PrecompZSTD.pas @@ -3,7 +3,7 @@ unit PrecompZSTD; interface uses - ZSTDDLL, XDeltaDLL, + ZSTDDLL, Utils, PrecompUtils, System.SysUtils, System.Classes, System.Math; @@ -267,7 +267,7 @@ begin if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; if (StreamInfo^.Status = TStreamStatus.Database) and - (GetBits(StreamInfo^.Option, 1, 31) = 0) then + (GetBits(StreamInfo^.Option, 31, 1) = 0) then begin Res1 := StreamInfo^.OldSize; Result := True; @@ -317,7 +317,7 @@ begin StreamInfo^.OldSize); Funcs^.LogProcess(ZSTDCodecs[GetBits(StreamInfo^.Option, 0, 5)], PChar(Params), StreamInfo^.OldSize, StreamInfo^.NewSize, Res1, Result); - if Result or (StreamInfo^.Status = TStreamStatus.Predicted) then + if Result or (StreamInfo^.Status >= TStreamStatus.Predicted) then break; end; if Res1 < 0 then diff --git a/xtool.dpr b/xtool.dpr index 516684e..f89db4d 100644 --- a/xtool.dpr +++ b/xtool.dpr @@ -24,11 +24,8 @@ program xtool; {$APPTYPE CONSOLE} {$R *.res} -{$SETPEOSVERSION 6.0} -{$SETPESUBSYSVERSION 6.0} {$WEAKLINKRTTI ON} {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])} -{$R *.dres} uses WinAPI.Windows, @@ -38,6 +35,7 @@ uses System.Types, System.Math, System.IOUtils, + LibImport in 'common\LibImport.pas', Threading in 'common\Threading.pas', Utils in 'common\Utils.pas', FuncHook in 'contrib\Delphi_MemoryModule\FuncHook.pas', @@ -47,8 +45,6 @@ uses SynCrypto in 'contrib\mORMot\SynCrypto.pas', SynLZ in 'contrib\mORMot\SynLZ.pas', SynTable in 'contrib\mORMot\SynTable.pas', - DelphiCL in 'contrib\opencl\DelphiCL.pas', - OpenCL in 'contrib\opencl\OpenCL.pas', oObjects in 'contrib\ParseExpression\oObjects.pas', ParseClass in 'contrib\ParseExpression\ParseClass.pas', ParseExpr in 'contrib\ParseExpression\ParseExpr.pas', @@ -102,6 +98,7 @@ const CommandPatch = 'patch'; CommandArchive = 'archive'; CommandExecute = 'execute'; + CommandInject = 'inject'; CommandDecode = 'decode'; procedure ProgramInfo; @@ -121,6 +118,7 @@ begin WriteLine(' ' + CommandExtract); WriteLine(' ' + CommandFind); WriteLine(' ' + CommandGenerate); + WriteLine(' ' + CommandInject); WriteLine(' ' + CommandPatch); WriteLine(' ' + CommandPrecomp); WriteLine(' ' + CommandReplace); @@ -141,6 +139,15 @@ begin WriteLine(''); end; +procedure InjectPrintHelp; +begin + WriteLine('inject - embed libraries as part of xtool'); + WriteLine(''); + WriteLine('Usage:'); + WriteLine(' xtool inject dll'); + WriteLine(''); +end; + function GetInStream(Input: string): TStream; begin if (Input = '-') or (Input = '') then @@ -153,7 +160,7 @@ begin Result := TDirInputStream.Create(Input); end; -function GetOutStream(Output: string; MultiInput: Boolean = False): TStream; +function GetOutStream(Output: string): TStream; begin if (Output = '') then Result := TNullStream.Create @@ -170,6 +177,7 @@ const var I, J: Integer; + S: String; ParamArg: array [0 .. 1] of TArray; StrArray: TArray; IsParam: Boolean; @@ -345,6 +353,18 @@ begin Output.Free; end; end; + if ParamStr(1).StartsWith(CommandInject, True) then + if (Length(ParamArg[0]) = 0) and (Length(ParamArg[1]) = 0) then + InjectPrintHelp + else + begin + S := ChangeFileExt(GetModuleName, + '_inj' + ExtractFileExt(GetModuleName)); + if not FileExists(S) then + TFile.Copy(GetModuleName, S); + InjectLib(ParamArg[1, 0], S); + WriteLine('Successfully injected ' + ExtractFileName(ParamArg[1, 0])); + end; if ParamStr(1).StartsWith(CommandDecode, True) then if (Length(ParamArg[0]) = 0) and (Length(ParamArg[1]) = 0) then DecodePrintHelp diff --git a/xtool.dproj b/xtool.dproj index b28a609..15ff681 100644 --- a/xtool.dproj +++ b/xtool.dproj @@ -1,7 +1,7 @@  {E490A6E6-3D5F-49E7-860E-CB57A73FBF77} - 19.4 + 19.5 None xtool.dpr True @@ -13,6 +13,26 @@ true + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + true Base @@ -51,6 +71,28 @@ System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) xtool + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + annotation-1.2.0.dex.jar;asynclayoutinflater-1.0.0.dex.jar;billing-4.0.0.dex.jar;browser-1.0.0.dex.jar;cloud-messaging.dex.jar;collection-1.0.0.dex.jar;coordinatorlayout-1.0.0.dex.jar;core-1.5.0-rc02.dex.jar;core-common-2.0.1.dex.jar;core-runtime-2.0.1.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;firebase-annotations-16.0.0.dex.jar;firebase-common-20.0.0.dex.jar;firebase-components-17.0.0.dex.jar;firebase-datatransport-18.0.0.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.0.0.dex.jar;firebase-installations-interop-17.0.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-22.0.0.dex.jar;fmx.dex.jar;fragment-1.0.0.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;legacy-support-core-ui-1.0.0.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.0.0.dex.jar;lifecycle-livedata-2.0.0.dex.jar;lifecycle-livedata-core-2.0.0.dex.jar;lifecycle-runtime-2.0.0.dex.jar;lifecycle-service-2.0.0.dex.jar;lifecycle-viewmodel-2.0.0.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;play-services-ads-20.1.0.dex.jar;play-services-ads-base-20.1.0.dex.jar;play-services-ads-identifier-17.0.0.dex.jar;play-services-ads-lite-20.1.0.dex.jar;play-services-base-17.5.0.dex.jar;play-services-basement-17.6.0.dex.jar;play-services-cloud-messaging-16.0.0.dex.jar;play-services-drive-17.0.0.dex.jar;play-services-games-21.0.0.dex.jar;play-services-location-18.0.0.dex.jar;play-services-maps-17.0.1.dex.jar;play-services-measurement-base-18.0.0.dex.jar;play-services-measurement-sdk-api-18.0.0.dex.jar;play-services-places-placereport-17.0.0.dex.jar;play-services-stats-17.0.0.dex.jar;play-services-tasks-17.2.0.dex.jar;print-1.0.0.dex.jar;room-common-2.1.0.dex.jar;room-runtime-2.1.0.dex.jar;slidingpanelayout-1.0.0.dex.jar;sqlite-2.0.1.dex.jar;sqlite-framework-2.0.1.dex.jar;swiperefreshlayout-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.0.0.dex.jar;transport-runtime-3.0.0.dex.jar;user-messaging-platform-1.0.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.1.0.dex.jar + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + annotation-1.2.0.dex.jar;asynclayoutinflater-1.0.0.dex.jar;billing-4.0.0.dex.jar;browser-1.0.0.dex.jar;cloud-messaging.dex.jar;collection-1.0.0.dex.jar;coordinatorlayout-1.0.0.dex.jar;core-1.5.0-rc02.dex.jar;core-common-2.0.1.dex.jar;core-runtime-2.0.1.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;firebase-annotations-16.0.0.dex.jar;firebase-common-20.0.0.dex.jar;firebase-components-17.0.0.dex.jar;firebase-datatransport-18.0.0.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.0.0.dex.jar;firebase-installations-interop-17.0.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-22.0.0.dex.jar;fmx.dex.jar;fragment-1.0.0.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;legacy-support-core-ui-1.0.0.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.0.0.dex.jar;lifecycle-livedata-2.0.0.dex.jar;lifecycle-livedata-core-2.0.0.dex.jar;lifecycle-runtime-2.0.0.dex.jar;lifecycle-service-2.0.0.dex.jar;lifecycle-viewmodel-2.0.0.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;play-services-ads-20.1.0.dex.jar;play-services-ads-base-20.1.0.dex.jar;play-services-ads-identifier-17.0.0.dex.jar;play-services-ads-lite-20.1.0.dex.jar;play-services-base-17.5.0.dex.jar;play-services-basement-17.6.0.dex.jar;play-services-cloud-messaging-16.0.0.dex.jar;play-services-drive-17.0.0.dex.jar;play-services-games-21.0.0.dex.jar;play-services-location-18.0.0.dex.jar;play-services-maps-17.0.1.dex.jar;play-services-measurement-base-18.0.0.dex.jar;play-services-measurement-sdk-api-18.0.0.dex.jar;play-services-places-placereport-17.0.0.dex.jar;play-services-stats-17.0.0.dex.jar;play-services-tasks-17.2.0.dex.jar;print-1.0.0.dex.jar;room-common-2.1.0.dex.jar;room-runtime-2.1.0.dex.jar;slidingpanelayout-1.0.0.dex.jar;sqlite-2.0.1.dex.jar;sqlite-framework-2.0.1.dex.jar;swiperefreshlayout-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.0.0.dex.jar;transport-runtime-3.0.0.dex.jar;user-messaging-platform-1.0.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.1.0.dex.jar + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;DataSnapFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;CEF4Delphi_FMX;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;emsserverresource;DbxClientDriver;FireDACDSDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) @@ -97,6 +139,7 @@ MainSource + @@ -106,8 +149,6 @@ - - @@ -148,17 +189,7 @@ - - ResourceItem - RCDATA - xdelta3_dll - - - ResourceItem - RCDATA - fast_lzma2 - Base @@ -180,7 +211,7 @@ Microsoft Office XP Sample Automation Server Wrapper Components - + true @@ -196,42 +227,15 @@ true + .\ true - - - .\ - true - - - - - .\ - true - - - - - .\ - true - - - - - .\ - true - - - - - xtool.exe - true - - + + xtool.exe @@ -260,16 +264,6 @@ 64 - - - classes - 1 - - - classes - 1 - - res\xml @@ -579,7 +573,7 @@ 1 .dylib - + 1 .dylib @@ -612,7 +606,7 @@ 1 .dylib - + 1 .dylib @@ -649,7 +643,7 @@ 0 - + 0 @@ -673,13 +667,17 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -689,137 +687,27 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 1 - - - 1 - - - 1 - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 @@ -829,7 +717,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -839,7 +727,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -849,7 +737,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -859,7 +747,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -869,191 +757,37 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 1 - - - 1 - - - 1 - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 @@ -1063,7 +797,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 @@ -1073,7 +807,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -1083,7 +817,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -1093,7 +827,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -1103,7 +837,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -1113,7 +847,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -1123,7 +857,7 @@ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 @@ -1145,12 +879,8 @@ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 - - - - 1 - - + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 @@ -1163,6 +893,10 @@ ..\ 1 + + ..\ + 1 + @@ -1171,7 +905,7 @@ 1 - + 1 @@ -1180,7 +914,7 @@ ..\$(PROJECTNAME).launchscreen 64 - + ..\$(PROJECTNAME).launchscreen 64 @@ -1192,7 +926,7 @@ 1 - + 1 @@ -1263,7 +997,7 @@ 1 - + 1 @@ -1323,6 +1057,7 @@ + @@ -1332,6 +1067,11 @@ + False + False + False + False + False True True diff --git a/xtool.dres b/xtool.dres index 41f324e0bb41574b1a86ba6253d4d2eaaaf710ea..26a7ef342a0b713a69015ac473371708e242fe88 100644 GIT binary patch delta 44520 zcma%k3w%sh*Zw&(8M%%bWReJxK|(4*2o(*LA*mB4L`y|W1(l|)R*;Zhn8ZY79MeXR zcIegHw{NTW=!LkHM34}d(5kq!LR(HcO3)J3_WYlH&Kfk|`~QC5{G3_mS$plZ?|ZMk z_MRx)+o|q?p}c#?g*+F>ah#$AaoOBNZag=B)rbjvz^YZF9E~B5FMQ)E|5)Es)=nSe z0WNFj7qeESx|q(Oe_M1h+f=HLd0C}?F|VlM?PA^redDQiv2~!)q3vVdQmKE;n<{OM z)u=(m_dh->kEMm|u#d;{GvA@d;e{7wYMA3%xE|JEUHfRR5}f@S_cx35BHvrrz)PF> z!2>HE;<<7Jq&}<_i>u(dRwh`^bK8ts4E4K;=d#;zT*YXqn=U%^KAubE?+9(+IIA>4 z*E6_y6S!tPm7L_zMVU%p#ydi})p)jX(nq?^CZQ|d^ASA+G>i9`&hwqytBD~|u05Dj zr9X6$rl~B2b@-m_7^rVwEu{G(Wc*QOrHbtj$>0-U@aI;O^TXNazto+q>t%kRm()Nnghh?#$ zNKfqJHe^@zsNZLyHJ`~Wh-e>mG9)!Oleg%vFf&Z+qEjDUl41A z!3xS#av5KWO{o!-8k@_$G}KmdlS5feOGhY*Qi(^gx#ou?9WhO=x_*5#t0kLjq&dUA zG}M9mFm=GcR5KncoY)LF#SCHWRg)h92o>Z0TUJ>fYak=F-aZHHKv=K!}TN=7-Z-G^-SVS)6I0w_d zv94N%g0vN(YV1q4B%MvEvbp#SXSJWLq?MIr{$@dBEhVy+646o_wIiXP<7)O-A{gY> zXLD#E$2D&RQ)6vW@!4CrIrwiyA?az1rioJm(!>rnu_oRYf}EzCHtw`l{}Pa9+PE?x zl-1D2o%W=vkz%Qf6=`HrT1luaX{QT4VPj3%YE?wB`6;#lEf*qBx$%@V>LaJRrtw^% zS*Wfy=U%T?ltR8nsNQRCKA~qBOnpFbUa$({aj!`b>*e7Xqo~|Y?EMwI3jpT@=Pv8R zHG=b|QqWEw?~iEmr>)5Df!zr3r=MlOu9@PQH%$ zMJ<%oIsCmRkzC;rIT&S--$Q`>W-!M&V_yWHLk8YQZ6qB@U2#aR#@n^VE+?Ho^0E=q zVvYBm%_s^d{}?FkC`{0;o-JK03=7}?wOZ|kLA7ZyiL8FSp^7E^YpKh^LHs-^Yhjn( zlm1i`Vc9j)E7kHIbR|JFJfOuXXm1zyBpt~R4=R>*+oYEl#&+vW+5+9#Jf@mNZzx2> zW2(rsZJvaY`aY7fFiiKbpA^2RpX20aR!jAYWglJ?%eN}5+Y;!9F7bsmTlmnr}(R5`@mgjvUeUuMeRlQ1u-oqS#TL#vw1Wndv{i0!ZUPcSTxLM z#AWz#7DH){m{frCh+Av;8Z4G>tzi@1SVhzts_Cr~^~VWMRSTT%B4t3c!4oIOBnqxj zUg+*NUfY74WokK#t6@FS%6eidhN)uVD<%G!Fx}-%o;OeE2kUljlCqwO(n*`7xz9xC zN;Y|nC&9gclT`IgsN>e?7Q~%M#|XWcg9TTx;C>)La0>yZ4mx=}I;`L}SP>|=^lVnA z@jk=M_z^1K264k*YSW`0t5IETbZ-+C;b=Y zI@zNRY056z&uvH`9nR{2=D=Xg?ze}r6~QJJEZz`Yym&%@jC>9nu%Z?iw?wE-dq}Qp zWdjNWMG$LQJxtd+h9J1@F;@8()+TO?X9R0|#y9{4tm9C4FD!KkSA5}C`4dgxnE+M` z>(H+RQ@T1&H#@s=oOJk^PGg>*1Z`UfzH>TZZ9KwLY6=Sk=j;eX$c#`d8^21K>-AhAjqz}{DC@9s>ih6}!u`aL^zdYY5_>md zvidR}0B&Y89x`_r?~nnNK6o~U|3*6aY!BUBD<@Ftf1%E}z8$j%tz4|Na{9nj;9p!d?*j!!cFaHIu_d*4ate{6bFT}~pUFhWJ z0zKHG36(uU_c>-Kyl?SIbdD1zOkihN{H)tiyiHEfh4Zu6$zh_=Ma0tyZC6ujJ0 znY34a6G~OFJfx@fP}4@k1BkqA?^ZSdO=&e{hEZ%Vzk~HpKB_J~YGy;_mN+%D?E<05 z7%0tqKHL!iNlrXo_?Ytszvu}$P|HxcPTqxXopc2KvBg%| zfDWLpb_^<(4jZJbB@sHM!V@j%NAP<*(*^wiU4g&!{SvEgUxlPBiJm;80yTi)lB!t7 z;T4q?f-DEP1--moUBBLlX+U{}l3i6G(`x=PXrun!55;m1e*?tZQ4saaBx$Eux_&M# zbo6{=krtZYCbS7<5S%puLdxm+&DwZY2lGyJe44o2hE88VNIZ_|00whGkU9s*Bd@Rt zfF>2G-qG!WBQS-ZE|@*XR&wR>r)OuRiS=Sj(q5Z;6rUd4V0y<^a(VRZrwd(vG3mjF zF;)ACH`wavnivyO_^{wKCXMGgCAMXRViIZ%T>!XZ&hsGl15#FTXxM=1JZE#(@o6qY zFT8sF(?mnmbm`gR0sVh@gsmYf$7w4^n#<_It7*&mP?K{NS_O;(Ft%c3a;~VS#z`lN z`#MH|rFW#_iuofgc>#Vf~3n1%UE<-20VM5Y3r1fk}OKB{OU`v}d zrfFDHq0wplEfZTOSJW>ctp4JXs+jGYh<;<9UXxTc)X?%ca`Q?R>qA;6dw!76o+Jp(~~pff4i3MmunLpf@#?)BoAS>-W9|or=*yi*sF5lbwPhk zOuOo5PiZkN{SYz$0Sq|b1aLlo)y!$zG`xoJ1>pz|VcjY*L%%CSJgPJ-t14Q&q(JJk zG$hT1e#RAa!L>8xpaF*25_DkFX+R&`xa-G z9y)jdwk?4&4^hF{liM>2VspRKhdZ(aEQ8|Z9ae+XSy*h`kn~Ak}Ov zgQ*y{zkQ6j?ujv0)i%8MBOn+0im>ctf$5q)pwNKK78-VNscU(yim{iCv!iWbDWTJB zp0(hqCT7OK5X4p>%{@}DSU3k$gRYg&I#v-nR@QWyxfII&I z7AV%G*%~#zoz%bdDP8eL(p#kw(w5S2M>R7D*e6%pK&t(z+Ai+3yDj?TielP$Bx5U2 zAG_(Z6S*0tr)x5tttQiQCxlJWT!y}Gw77C9^B836_t}&)cGr~4X|5?Za`E+7TG!J`WLj2W{CPy0IQsW%{uz)$kwDfxn5_^i_L7;_Uc$tKkMaFA}a@fueooU%c%-Oqs zTpUrwqu3eFGtn8&W(#eucTW>k^mKIpdgwg`nX{1UeeADi~9o+8LMOq`69L#Be&+fm@ zqXg$!3C^<;oM$CCPbD}{B{*MFrHPg_fl>+n4+VJijsh$`?<)X%vpUq=vRADH>`g1d z;`3?=7(odbxw8bOPg}J@s9q1`2Cef9oMQ{ouG=2O7S^;Z@-Icv8q|aaHQ(8w#fY$Q zENer@tzMstu7k&BW?xqJmB_wQ*;f|(%3)vmC^d_XQfs7@kp(-^Y)Iuyi`Hy@tl7$L zm0InQfyEjr*T_1J^r+Bil|~vg(xei;1>Kpe@suCYcb1mDaChLO_fhL^%get@yIx41 z@+~v7Q|bx~@(K70)mgx23W&!=qahtD=MAMGiOn(Xt(l5nzTGRIll`tsc)@>@Di~d8yo~LLxoc4k9DIQC08t0-(`vygDbYm z?SrI`mv^4=FpzvVQoxKiZVRg+_m~p-6_~-IafFryHW|Wg(IA^NF<3%1wWEqK6k)CW zAc!qqqiE~|S>*KsY+shOU2vy}hZIY}yHc;R9kDru@}ReVBt3 zk&YS$H;xGaEE5745>)^tH6o&K8tLa^>QenwxRF69^Q#fWAi)(-W7-&0#w77tu1>?@ zS50$+(1qtt)Uhrc$a?cc%*aB@;RWr-xkDVfjFi2mr8Yl!rkukk*wk72NBBF~ENI2h z3}Y``ggQ&VFhL#7kD_Pn{(G(tv)J4@x+a=0O)u<1=h8;`f1S2XFQAQ7y-;Ubey>{3 zjytOwN`vs+Z^hR`srZ_Sna>f3X96Ze#}rJ4ju|=NuPBBl(3jU~^tCFDzSG3$Uk+EX zMpv!V=-Mic{;LU;CQG4YCx=CcO&_3YzCcXQB6R!zGV{0GnnGByR|awFY`MgNKB~Qe zXT8OvH}Fz#@#qb_oLjschxR(x9#qU01-rUZ0AZeGh!G%ly0vSw?wmW;jdIk%|E#g| zsIl{`#?G@EJO97dn1ib9JXP6ws ztBmp5RvF__(%}4}) zOe(${_h&J=9442KkPNoSO<;@M6t>9C@GWxatt=C5*1FaIWvxrTRqZVQ=vQpWqn0$J zE_wDqMp!SqI!g^)r$KcAvho_X6E&>pYW2IQf(?dj2E#G8>Kg{moqW8cm`*r{9szP79rjzGsSJ!i_S0?MuvFE zR>_)IvAEx0D;L}P6h5d}qJf;PlM55l+>TNH5W|wSn(fCfVK;yUD-4T~8eOqWx*f4w zhPcP>9zKd?czLOsxZvFFmyvRz`7sP+teXgpy)zW50@aO;@;X{iJgUCm#5-Gbjo5x< zEN{P3Evz>vbNQEelUCLBceG+tlmxrqa9-UVS!57dWDX5PW2Y(VMg{J+9V~Oq--7oC zpcZC==7T^RxQ%-eZ8K&y4w}BwG;L&>Y|sQ9&=ul~yzmZ1Jshg0I~1*AikKH1iWX~% zmM}#(VA!=$)b9>O`(9I>f!Lh|s3r?Gg~-)Z#Ke;9wmiVJGEV}uj$rCOggQa|M(lWq z1J9)+r)CGFh9?^G{!pp;>U%raQYmm2sphX0@%aaAMqpgw^ST4LO zWtI;J9I44Xc~x3o-bZJ;Dt%F&==ffB=rHe7gUsI%o1$)K!E8$iCx-Dx20UTp9}vlO%m^VV>isTIDST6p zU`q#95#(8F?g=DqFoa9PR>kZ7wN`q3)kxj6wbCc6qC4Q=2bxfQpw0DomG-V0plcuY z#hg z0ZhLA9>dc}5YC2C<~<;4oytwpl+}ZDpRJZ&TK&$wdU!6Jt$ab;Y(eyk&0#fDz75|v zB8B6wu!CY*E3vAr6TeX`ui_Q9Z!nX>Io9;m(%e_Nj6L!m8>@nG;j+!|VPQFS%a4FflzZrn1^7wR2sPl6L?F~(lKTpxve zv|vbd7R1bCc_K8iSr4JI5GajX+tV7Y#!}^7mPi1(!yQ6~Kxx(5UL86k?*)EIRbD}U zs~3v2N~&8M8*$UdvTis9g7U1z^1zZ@6(AX2dyJndJ@wi^{w`_VYrT6MfUn1{|K0l$ z3r|yvC9`u?FHW1TBI+mpQujR;>F?LV{UnSf&-YL0yN5jl0m1FS4$dD9UwDDVz0Y6D zdOcFNB-NAnq&{-QL#fD?K^9p9sq=&C5<-_BfB>1@YQTE}d#_{fqQ;-9y+@@=yI+rw z`qn@LGMshCR*cB7c6g~(J<^^tQ}p3d#l--r?;D+U&c4#nIM1QBmzNEGyK5f9APD*3x0T%U^2Q zoN%wS`OUtLANxQQCH)bt&*(?$)L1Xi8mM9a!5Xc(t|)gL5hU$x-ra;N3%-fbI2NVG ziAIa5n#Z^-#Aq@mgN1&0hNX~FkDcQvO!VkIR`7kQ9BR%)<0SM-IfjLc#tE#4I%91h zoNU&3MyAUdJ6PrYiM0oEyvDnXlQmA38P6GefdQY#4ug63TVT75+u<&5o|cGJP4(d= zu^y*-{*b=!@M${QpJFnh(!B*?f^+zn=%K95zrXu-sM|7EQ=131OhL1gsU`>EX(+!# zk35$3D={@folO+WyCV7_C(l6Zm#01;&3rqeeLpxKwuXU$v8N01d^}&@_wL`&9$VRB zn*Ef7EW+txC zXfhj$`m3i*?Y!gU;q9bJ>$926 z^4<#=r*(2~V4R7o)4w$DN~q_PC-weazlCXr_0C@qF@cnnp@~`)4uji_(I(m7iHTgT z37A+Cf%1E>QP^sH1YBvwJH2{L)PhGNwwbku2yafH{0NDyAThrX_VVXDJ;KJsvQbrs z+czJxm{1kRf(HG2a<^%0T)5JkToc%s@6Q%lF})FP2B7K~>4SeS=0BBszuR3`{HiqO z-M)@TRO>BM7Q~EvdBSawEvMZEIr5O(AW!ad8whgLZIC6KZ-XLPe+#JlRQam9%Cn?g zRT!}HkHFIJ?`Apthp=3}RE~NRQjD))o+=1Oo7>~GQU*JpF>{3mxoHr>U2m&mSRqka z^>QlX;mm-YL$R}YGJlO#Jv5J1ANhrzOhP?9i4u+L!G}j|v&yWOpF*_>2Iw*VDee7F zS4YiKw6gIj2#Jl%;T0_pP$gSgCxY4CsuV6rQj)HD^F`xmND0n;iu@o%5PW`1ogPCE zqm7eKVxAU_u}nf_aSP2zv>)WK{)pJAdbbGTF*!<&SlCfC-t4Ys%Mk(&>@_dWat_J$ zs2|8rg}fkMXQoI3vL{?WEp(|8dhRJeS5faJ;LJ=by*ve%6>e3*dgx?j=psMe<5m^m zk`8=*i@i^Y#xjJf`S&0g`On)@=eSjXu4(~tnTWRiVg;BBL1bwEA1FWy>)&MX>ismRuqKgW9VKU|}VWwfJ&Roi9j?dt!kJInfitNwaO zso6gPkv7#o9qWc@{D4W^uKv#Rw;3Pzm9)I`&^?%U^|V?(Qu81O2a`|%VgD$$GOFRP#^kKT) zOC-aGLv=4Nky1Y#a?ge(tVCiftXjOWqfbGx*zw9b+%lywgx%2dM@v-hXJv4+3SC+!etQTo!s&;&k{kElZ<#4#(U! zai3kRwI^AElBBGUrt2~mOB+7w86CQq#jM*Bu3ZX?!@M5|*8Zd_Z2MkX<#~9Teq4_l z){Bv^Kn(2qu2=^Aa8D}6l`MbA0^CtwboJCv)A#E4+#PbxTXM(=-4c2Dj_~PSrA;4? z=>2Sh+5zHNpL^>^bs6Z2nf!qybs5+KOA#~r?&qbbPjYpEze~$L>0|o-H$^F%9;fb( z<^12I?Vn_thGJ>8&w}|{7^YZ+=cLF_V~v@b+*o(W-SQd9{^@9bgS7h7$8@W%N`HMC zZ+aBlN2Z2nZFl_&)Z8WIeAd?#YC(<6lA)@zVyS&bdi}FF<6hWSkQKxO@*`KIhR??H zC6cvbt?v3jX=}v>UXY&JVCNS}TQ&^Qz1|{S-Ow{^VGC+Tsm*jR#I=Q+g%{xPkY~0? zeLvsHkCZNb-j$D)LcX|LH|CO*_QkMK-*!ZrNU;a{EO&{ucfH(-MRB`k*~=I3F79d8 zVM!X}4rviw{(@^fR)r85)Qm{=7p0mnM(Z|)N+Dkk;Rj1;Up~iYN>yJb@V%vrU(VF6 zFOcs4Y6Abb^zK)^v2)n}mCH2b0&BZT4Moy6v z;nuuKNO;NY8%f2eaEv5)CTx`kytAX|e8uv@JhkiNdZgbx>5o4mS#Rb{2Y5lBB#j^ES zca|An=zx34rP9DJF(oA*xl#cBswm8P#=mE|5fTb-SW_Fn%f}B)W;8Vg? znGRC*ju`%obZ$pnhcK2BcJ7H3-V0#FcqoW54Kic8jE6zwx^p-hbsLZ3-7Z#r&TodLqsVvTdyk#RN2WCqHy9Fy=k$*+0an-+-wfO_6VY@rwkq{}O zdVsDbPa0dDoUj;cSMyowAXavi_fnqLMRuY3VO+Y$Mc@Jp1mt$xrHX22)ZypRDX|?l zRIwLW42I9x3oKQ`ThB{bJHvIBuF~T>qeA^t>}u6df}XSWed)QH;kxWoo_-(b^TTwx z=umJPL^9+0uh}YiI#NpAHPi7L5ZMBg-6gm=rleziY~?e=Mw{u?ok>c`jln40qOR_e zZ&NV(S_2n_*ec&g_1H}Ru9~0ZywS(otK`O-@32z?{on^23q+LM_=3@}k{j}0;I}Wi z(adN&)0Qj#UY&T1jYA>5^oU&?1VlxzTUVVmmjhS|HPQeIKfo2$C`iy!MZi$I~!_l>TyA-btx4H|0jEZ3T z?0Z2x$c(SwdrAM=(^Yr6QrfX+M3+xj;RMX*iytel{JZ=G);TS&^1Rf!cG$3%W$0^2 ztUYePnMyxKcWHhHUzy^U*UHbazJnX!KCgTHOqL!qnG44&mak_>t7P!wT|&n5q~DqK}C5H67?IM0XA>!U1cxxER9 zVM7FOC0>#_&#CA25qbove)>PrbvJiK;o*$FfOnLn8ARq-3nC|Bs9mA98e@$)dQ>lc zGg7LI)zPJQ-1uQL2WEibh^M;z6E!^pn4U_u?}46RHrCN&K4y`5q{nd0{vNmdCd}I( zffzn_x}T=RJx`jkH{9_kD;=fZI^B1BRXrj&{wyNFm}4c_jdzsb5v+Z30#a_{+3$9T zmVMCjkE4dQOb;x5re~R^rx(-nGc0%M`d-t6-{suS!+bUNt)D?ywR3@qh^9L8Z{|Z6 z)(xg>2$=G#P=cD&eEK*RSOHz^SYY7vJC6nQEIvCHxbh5=QZ-dDO|WJ-`5vffb1V=~ zTDCr-9ScNCTMZ%6SvFkvVWu@|^i5S!GgOM~&23rvjHIidrdvE+no&Q{Zv!Gqw&ND* z<9bt4a}i7QID}5)O?9pyUOnDr9Wykw$_x^ZDVA%9haa`{lrGkXbxQ7p(d0JRLP1in zVj4VuO5yu^O`qNo3^ACw70d!N@=@Ng(^wt$#DnXnZo?@ig4>miJ}(%#jugugCb+B$ z^JE>=-@cg^TL}oSQq6u(d8xjK?vbfd)&9FpJ9}_k%J7_Uh>jmVC0sHbh%qNY1$tf| zLEMdU#CDQ~9$2LNxTEy>fk_dWQ(0MM-13K0{RY1<7>g3T+f$|JgDJX^VbautLXh7# zYz2Zb7%Y8yuyfzRfGcRzcE=@L<_jAB3|4DF3`t>S4U!jGrK<-8-M=1|h964Q&3#y! zdua6dUJtX9W4TBHHTF%O4N>4bO|~xd5+ca zH}#@PzHwNZ*Kl8WXU1?DBQ?@RBV9wKgAK*H_Q#~G!*M$6lhU(?yE~?x^w}lyz!R8m zKNZ|X0qFy;rIi)LLd2t84+SRCEMJF*vC*dE@Qz4+s$m9>)d-I!u@LoOY@`e4nc z7So{SaR_6ln}Y&8<)+frh$Bc{j|?8P>RWW+R5R9+B0N^P1h_WU zyb=P=n*oIbp`qUs5Uy_W%h0EzK8?Mfkd__k{y@Psa9#f1cD8M1xSUvWL0S$zJjA9Y zION$Cq8Bk`F-4u_V3tyrnJrX%FdaUcjrCD!@U~YOIK+7%Te@+iyQ2;*iZ!~(C78Yc zW?BQ>i?!Tz=_0bkDsQAPX7FWV_m-QBUB#u)n&^ za}A%wYH!iE4oxzPK(qj(3ZW$DXQfkOLjPFhI^ekAtCgW%{s? zUeK+X{qjxehvR)m1|hrU*vX$`_Og5M+rP0Mj9)5Y&O6CiDUCQ68kz(PMEs8B{T?h} z?Ds{eDerd};N9vZ`-v{P>LBUy6Un*}`#o=0z{uKiB2@QCkkoKuf?oNJ^F(aW&(weZ zJLg%vL4TKVU@fa55%UEn-!JVrY3XIYgELHR9I=Uow(hwtd0~&|;s$+>0d*P=9gy2m z3?U)@ko={_V;dp{|D!>+!;{zU@z_4sch`M>RmwjV7W9Z3)LwpYk5qmtLN{!W=dI86 zapsO-WB6cF?23XH<$1pQTpzC+njSE2Bt#?fJa>PgFVRoC z!g;oQq0e+2V9oEYWbRIFha?(;u3~zxW7cFeGm`phhv$uK%wiyZ*^V{aAsNL6ZPWry z_^k#rno#Q)thXGc^6U8JCkFbR%lRMd&i8#R>o!5`EWP+chyDp{4M%J*%Qf>gJD-Cq z_IqUa<|QP8CPn|QmtTgMw>GKDQ~V1bA)Wo9qps^O(zPGD#~#Nhr%v)I#$aEiJ;QT{pS{fq_J3WmTrPof!>Pp5+)u%^~#ZPxmeK66fMna>aE)| zMq1f4xa&bkD;C!nRV*P>+>?PHKBinR=wTNRO5ZodIF#ISA?51As0{rNLdp*dELu-h zV-3<`{p{_Az>JjBi(n0@pHFnvS#ZX{HAm$3liM-<7dV)UnmPH*IApH(jnc_Jew6e<@W{F zjQEQ}%Eg5ds`dk__O+_^0_2U|CXc_Ek@730u7y97Ass)H5~#pBw2R*60x9ZjkHBwL z^jC9+B%B?h>)?_~&feWVW1ph<4x^&>O1salj(C4BSTHrX<|)VmGkmi#<8Am^n)hSL zw6~TcSI%A8nC7dz&GN%5S?B-9N9|y2pKhMa?iaZS-z;DU%4c!N@kd7dAIyjpt4)(n zX1GVP`&%aXr`s=DAeO%Hr(1g9r+H&-n{la>%QY2L=bDaHv7K=e2BT?PL*}w0h3(Sa zQ?7VJ@gsS0=VmkHGUE3MDb+U9%QZH!I)0}O7mt=70$*JDLh_4}>F51^Cr!)FNvD4f zpE?+}sk%5}U&p+i5!0Keqe__ChP33#SjO2VN!|8Qa4LdL+we2`jQE{!Uc$g@H80wn z7Qff#ykT1O%t&$Ni$yrC74!$Cspl5+gQQdEdOIS~xT$=GQU&kKw`_xaD>-#Ino)6@ z`Wm#|KpYPa#gmF>G@fyICgGWiXBM6(@XW(ggvWvB1w3v%tMOdK^B0~F7#Cge^ucrY z$|vyiaJ-De^AMg~JoE7^!LtI-t9ahQ^C_ObZme#2?!jZjBj6d2=RrKv@H~p=Nj&rM zJcFkg&vHC3;&}y+r=nKhEuK~qVF0qTK%mun@(PPRVr_e)O9>5ecT)|ysr)Ir)_>Q6Lk>WV_Qmw ze*Zm|BZC~XSy74?M{w9{iRqkk9b3l!V!kK*1~Tb@{6UGt|6Edz1OYQki$M?wQll8Sf}-S|X|-zDaOTg(G~~ zNmJ=-=nl2|{rj;E=p@hFsAgKqxo{VMNLq2BU+0`gSXOY|6z2RRhl4jthcAS6jM|7$ zmahPouP>Hh6&}TbI%o&LED6$9419y?3Vr5mzb&5#lv6T z%HBJ`N@-e{An)AHRE407S|?3JJRGRW&t6b>{HIyFy!DRyj(b|h2cVhFUt%pgW_@ay z^vcCV-GV2i+KX}RtT3YGzS8d(yLXAhD}IF)HvA_jUSJZ|2zvb8h0@xpR!$mlX&7vl zr!V#CJ{6X_;2htIV1wm>^w4YDWifTu{)S(IG+7=1W%8igfal(CO$S(WNCm{_<{(B}>CE zcW=J}Yx068FjK>jTyja8b2)}TC9S#~Z?z+%8KU1c!D(-egKx|mCTDDAmCQ0JciSqR zy4)x3$`-7tR!$DO72+MYY;V!P6k{D~ctIaw7Q0Zm?V{B0O3%>uNq7e+saY+}zA{3W zc2RooO1CIG@|biIU9g*c7ertK;8#^RTXpH>pdvn)*>+%2;L7YVX=yMyg_1v*y=+>R4=&of_EF~B{3cUeSv& zf?($N-h&O5;rWZ~Zz#Oj1Vv-T((_ExE~bBGkFRx%G;OrE+_Y>D&-*%|N~@Y|u#ET-EKjf}!~*TmA+V&7Lb_42(A78x7&bj9wPwgV-uwYxKS z;Ln5L?4{P`RQ%MVKhx?er3!zjB@MqLnT&zVPI^tH$50#_izz&CiVkGJ1d;FZJA_JEK>(z^UcPTsr28FO2c1zNfl4WPdjbXpGFTIS`cG5EyWPx zil2{33o6WDYVb>7qpB(wd;YX^{4Ut8DV!LSrdWbVqXQ~gMCbD{cHXq~Q@o;IDK%;M zsbnftGmkOr?o9~5;&xt~^ii`PED+X_VdW+Wurl3I-_L-D#&3wSbBwt1N3Gi&MAeNf zOS^opFl%Qn2qO{qqgJ65q=x_mv{ zaZ-N*X{RMmdkLd4)tj-xAKmf+B;xCqN)5tQ^%x{t^A_7%hWjN?dda(AvD6K~bd&C3 zky+z-1K1l=huag_%4X3LjRIZ2VltgEX#bkK2plN5gO6}qgT`N>LFE2 z%z0>8p{q;u{0f=QbNc@m6}{(aD&it0+8(q`uFPlosp#(0wD2J3nYKzFt@Ar4?fYXL z|FZPt&4Ie<{iU~WT6KRVNYytx`Blz^iWgtjca?s-*=OMP3T=V-4pf_zn_ohYz~sFk zC2Ez7$@@ulXLnVo{u`5R!jhk)tUo*Vc6J{mk>6q>qwL{~0kk>0~-CanCvfqdlFCg}2y*2TM6xuK!F? zu;Ts%LT2CQPmy$;()>JRusy;0;Q0~VylF(Hcp;)*-lG1U2-Bv_mfV!;!n>V&bgsg0bDcMW3cGAJ zLYur1ri=Q4`O9Nnzc6ZTlpdy z1_Oi>vjdtv2t`?vs4S?A8_A2Y$z@lF2V$#S(3xGne&(MF*sUAy*Z<`G=O%a`{F67& za0AR&q)UHChseketcO6H<@b>ym8hAIis?;oCnq>t4TW9g4WIbBp!|aR%1#cNcVbQw zGn?d|U^RaQfUHhFL+y4gtu;Bdz)(CQ5?86_sgc;hi+mqmomQ@Sne?g>o1~6m<_J!7 zJ{n`?Hly-5c?68j@~!9(94610l&CI0;{fj|Vb)Cy>+qkYOG@MeH(^!64R=F+2X-5~ z*W{<9!QUhr&T0Xznm_K{yO@~qfKPfIG(+fXhMh6394x=(TAD%cyfT%)TQ}~VS61`A ztoOhu(T1FNB*t666k{to*@9lM$#7tB;q)sf_~^KsANhpjh7Z*vP}N3P@1dm%vv9P) z{w$2`?M>Zzejd-y^hi4X6-UfL6viNLc$?*9tQEK3A~nw28Yd1Mv&M029Gqbr+i9HV zG)^)&e;!cvJg#w4!MUPwCTkp6$wpb@q-&f!aIQHtR+7fb2kU~y>8^2#z&WdNOe#k# zMmvCcRAc_}7R$F-rZMX@=Fb{a9hfP`ts1j|G4Jqn<*Ju0A7W=N__G2jMuR5yp&EB= z05~_X!9vxn(Kt9eFkaR;FKC=>aC&R83N%g*I8hpB=3Aa(KR#7weao}ck9S7C{-&By z{3Zd;W9>bQy*uCZOgHcodL?SS^_Z&U8121+y+>&8?8hW>$eW(;41AI9+z+0tc6_vP zHax7%wkqr(Kl`31v0C4))BG59;J{um_rz3DCm;WhXGXQ&qSKx5>}to~8~GNxGzL;M zvqF-$AO{%GxR)Sb3-EOH=i_re`OmHCbLqS4^y!@-+CnjdSIfrre8D}k9@YwWJFx9; z;G~`Hd(yEpL{d93eJ-nd#sFC_Tux)z)({6WqhDvaoE3+jUoY!C@B8z2g~Z%W<%Z{! zKi_pyMG$79GxBEm|KUNbm*W)Zgmqspe-0K_hC0nfXr9DYR~s9nsTevxSD^sf#RWLc zkJ#N&H%~Fg9>&G+hx>Xm1NcGl<50@n^q~*5E6l^i6%#HprI+q43TG$U`ZQO20GsKR zof+cJ<`~bX0eqaJ8MR`o@PogkYBSr1#n*{Pzd37L(HMW!*7LCUuJje7{A^+;+lq(V z+1#mr57U{JZ-iLtJ>Q#_e!}A3(`a(Oix<;IZ$`?+H1R;DY1BdZ#MH!BRL4{eD>f~2 zLO|S!Gm7e_cH-gS26iMJUz?Fqt($+$Gc}Np(&-h?bAkMXzWx8r+N2NU)qOP#nM7tJ ztcNkgyg2L+RHQQ7gxMt?ey_fhe$+|M6TVm9E$k8wHFHgIa;|A&!WZg$|Add8^^D!C z?=kxr@D+(P+Q@3lG%`ePp+l&oGVIKO>oQkRLn;81Wl(O(&yTp7r#q*AJgLPnDT!d0eIDUl3+F zPy{)_v;>JQI65fT^hbghp;t}kes8|~mjYjJVK=BV-UqQXoZ%o<`4@~+ui*O?ISUoE z(Am#=szdm&(9VFkoSb6d*vmm7*wDMkbE#h6CoC9@-G7|By8Fh-ghPw)exrXv?IKVA z{rbqbn+)Ha8tY^-H~J3_Mm|iXFQUyMZTwZ0J{k4fJZZQ7$HX)0{8?bl2I-A4KKR zOM-&*P_!nMJ5m*!0&sY9-LA7|Ht&D6J@4n3qvqJ0@Gj`CvwJ_f-M#}$sC{>PvyUQZ zvmsxV&EC2D#@qrdzswz6j+8A$Du1e>m{D(b!|t1p>;pfxm~ys8OZxKetJ!;z>%+Uw zHh&QNfSOA~^9Kn}6nR!2(D&$*tBOsn*?ls*Y4^z~#ytz6?&Or%gN*(-#h~IJr$l|a z(9?K8A2s?k<9nxCjxl<8!I43`n@?KqVc# zs1Nm2nD{_l?;6im6Cc_2VT7b7?M-8cz+2FuxHgpB1>@IcxV+PI84~S}?qu`KjlUM* z#z2QIp3Y`|kV67bv3wti8LJU?*%OLoMrUT|v@4trd#3P?7#koXz<5f;x?=noWyO0O zegLLNT5Zx@LZVj8Gl$r{G?yVBn(&)T0e>^eGFMHF-2u+_>^Jj$b*D~CFJ1{7Pz5)- z^Xv%09g3SmepT1f8j|^fKxSL8+l}qz2CO%lvE-~*aGJNxW9z`j#61VL)5^)xtzd6~ z=6IU$&27h1`XM-;MtTl);Qd2`C`ON4G34u>UpnyJIyIhVOBxnlzoe61m=K0d2R&Un z@;zx@u<2=!w_}_HKuRIeP^rB~7C~x53_PiI$PuGbLdi*VXysle=r|7W$-uNF- zDKYJawM#dpRpSqK9RdW|4+1AD{xBB%BdHhQa4;NodCnZxOP<6qJ~!kS*i8#Ub4~pW za@nSHNAzR)O}~co6L{TxvnRDPzrDRqVs+UARqG9wfA@q$^10!QD*+2)uT`fOSmnz% zVY7GNW5}!YxFY$ScG`)H=Tam;Tfcv};(7g;et3Lk=0oV3L;1pP!mJ6y1pVQXd$Q8e z*G)-Pn}c;MXiNm$I{4dNH6H&){llJaUHK@#DdVyGeCde3tLMp+`q*9z!g6gTXStS4 z_v`pgi(f;i;fWv=n;IjQgEY3#~h^n1;SlYmDq>Z3YM!C6o$ z?r~j!MgGf*OsiZp#Zw;5hqi+`IIa1($8%BNt>ckQb|#gcFHXvX4^x)Wk1gU0+7ANE%0mX~+am1kAkhrpxmBPv=dNxG!U3Fh9^&E=kBkXIJ z-kH35_Nr+r{zt%_`MT0@XB06rON(%_!SnPbeR$`)+eFM%UGmeKmw0Ab`NNK~X;?n+ zd4uRiqWg%RBkK2vCKo}pFHwQ$Ork|ZUnIJo=yswXx8lf zg2(4RO{us3f8=M*c+@&$&YZckW@y4i8UIzUg;NFR&!6#(_37D*taD~R{a8Vs#~RCr zI=C@flsu{rp_i5wU%4wzXc*R?`l%w!wju0O*CxD?Ft%RmM<$Hjl=^A?Cw6cFC$$9F z`5OB~5cVmEANpL))Uq`D93$F80(7qskjrfvGQe`2$gpVAbeXJT-65G5%x9Fw{5~33Hvg1>W*-h zKG$>ygW;drM3CFy3vF-HjqTYu0p;ESJ1*hJx3Dn|YLdhJ9K zFpxk_n+W-YeHmO$*f%k)BkXH}3c~G4Uln0rhME{=?eD7r?zEQUSc(uw7#=kBlhr1? zh%j8!>SsM+UjlW6trXrum|4&46Vb#KJJ=A5Qz5oVU@*)aJM2?L7~3)Rvxcy5s$NgH z1BF)+_RXYiXU>K;@^TaBP!see5O7AzfUlET3Hxd&l`yt?>Zff2ZD+c+6II(`9&u`b zmY{E3cAd&8UkyafXSq$LBz~~Yu`5kW zfHKUP)BlO>3p89z^iPua(JuupJR(D*yGX8ys827pFKSJivovbgXfOP|AB_>SP@_|y z)Tl3kHID(qdg(OM$MLCL(=9 zxJ05wUT)K8$-Kqj;4WzrenbO^h7dIq4J8^uv@6jVqKQP4i3&s~63rnxhiJY=9b5r1 ziikc>w3uiq(K4bdiIx*xO>_;>bwt+_{gUW5qCWk*RE%k9KLvbCw25d7(N>}XEm}>5 z5{)2gC7MW7AnF@x*@Sb5<`KUG@ED+(LAF0M2m=)5nV^Lf~YTnD#8s!n~1g$4Y0u3b)CT${z@HaQ>Qg?=!3d2>aGNGhv@C8$sB&2F4Ngt#OHjeP%>5 zVV{+dN|@F_2PY82XZ~jq_AL?Fgndg&4q>=T)K4B^-y)h%*td!o5jK;2F=1^*G&CX680@gRfN?=n04biV))j$2Ex8It&yFT38xXRAbcO;O2VTFR}s$8%8x!kj4>q8KzJtDeZcV$F#?D&o3Pm@KsbUhPZNQau#RvdVLjng!hVFY zw5v0gfpC@)HBZngR6V4|bK)9H2AYn{ls={ExL*C6Vhdq2;V{BhhFSYZ5F?QUIulMM97#Bf za2LWkgu4>XCmcn%m~c13<%FXNuLD+>F)J}DNT566D#ASoHxTYgxQTEK;TFQNgt=g? z26_=T6Yfpe8q90f{awUJB!NDJQwhft&LZ5Ga1P-F!bODp6D}h>knkG9g9xt=c4#>q zOpHnrxSMbt;UR<@2`3Yl2@fUQO86ea0U?_H6v7dN?*Ft1u97=IPS z@FNq&w<+-_Tt(r5gc}G45pE(JLb!#nnK0L0%Wx;c_+g}40QP?os_T!H7-1xkNH~%( zetD@zh$5U#xG&*6!YPD{3ExY&oG|{Y67{o=@RPvm`cp{^o@}f-!hVDs3HuY42?r8x zB^*RJz@%k3gm47mNWyW1Q$lF{O(h2VFALcxi!e_{TMl7A!uf>#2^SL%BwS87i10eX zA%tI+;I;l)}o*Fbm^VKc?|`3YnSPp9xW3iktM>2s|l;M?*9 z^wg?wIYmgM2tG$b1cg&yxsdx`LItV;g2=Q)e+uHxRLP7gk{28 z2)7d6O*kM%tI;nBM-cu_t$#4%h|xd-$%Mxe76?}n&L)h@Md~MuN*G8ukHQZVE+YIr z;WEOGW5ifPjD3XH6aI>DCE;qqb%ehq+(`Ic!ZP7c2)7bGO*kM{tq}(&5hFrla7~2c z2yZ2vO!y#Sf$$l^*@RCL&LjK-;UdDv374rD^}mN0RvMQ+*UB1-;M-U|@AegXB;j0O^68;b2fL>as z`;>45;Y`AD!0M)QBQcUmU>jkdC*oeh0)>|m_PIuUzMO0d_jT$#%5VtD=TW%NGg2Ht zn@XRvsE7m#C_)`6AWMiVqwvotd<|irXC$BGjTF9~!p9R9sD|SRSN5V!l7Y1QS5DcnTiGKIfFxRvk(!etJMU?xUDZ>`PlC!9|iN+29T;qwWX zQ}_VFaTH!exRJu|A)HL%O9?y5{j~%;5QC$sb_8L8BDe@=6Mlg38cJXw;XDd|hHxT< z#}Y20@W(Xl;NnOih6Kt;AeZnO!Y>leA^CX1>nZ#x!j*)bgj+~{IN>@9e?i6Qs!b%2 zLIRB>P)fLr!V?M06#gdRR>F%22gGSLT1Ys8@Fc=({I&3o46~sUM*^>sKr-QpgayJL z!s|#uC&Jkj?j~Frpe1n21RnM&pagpm7AX8t!r6qk6V4-CL%4`=1>rKnm1_NixrP|^B(R=v zE#XGWU?ky63ZF_ig6#Vr3D;5hPQp!uzb4#D*zq1Q%zd;fTkDHJ33eeIN8zs#P9{8! z@H(oYB*FrPKTFu>v`!_Qt%WUeDsQ_8&wH znFQV<%*AU>@*Lq*O0YNK2nv6ka2(;egp&#D2@8bJ63zx5|Nq)M|FEj6bdRqMM+C9( z2dF5HXlPh!h^S~zf(H={^9U$p+~nvv2GbGF91e!29c;3wv7)lJIo(1a#owe(V3}fR zdCTsQ<2+-(?Bvul-kbM%?CO*=d7pbf@80_;m^1xz|GRfTe*M1dUGI9=+UwU|XYI2N z8i9ICQDzxzvcff1c#jpXv%=e~@CGZKZG~HI)!J^&vJD*>mLXr2EV6mI0r@&Ybm9r? z<>v{dPjA%L_VCZPq5{cQRJz3(7Ux*(uvqf=X!$v#cEgZmu9$YGrlw7nTeuo0iw&9C z))Tk>nmAb(oF{P$aV=(CuPaV(q{x-}*15Lc(UB{O=P(^#?c`gc@zW)T4mn(BU7cNI zQHg!%`fo0gGl2Nu(i`U5;-Z5qG?kdEDZc+HPW8^cHk&;iMd)%MU+Y=oBw@&^t9Nn6 zWqMDc2n{Is)~I+NB8}eO#rY{@D+A)=hKapYqg-8`iVrT+X(>j@vr)`8oEnS83<+D8 zql+vmu@7Bcofr`W#P#7$*EA@F4glGtXn7i19Ofm{O~T zFj*{7DwuX6jUN*21^C0XvO`~8*Pk{W+yrDsuUN@_~U60a33shN~klJEAoomFnr zV4T;r7!UL|3*@g3f?4*$JQe6e=KGWy=Xclmla?7XS?Lk8tA%t)DJhhcPD4slSHmq( zU)^#tvIwS*4CVS6{jLc?K4H;09W zT_?KMYBKRj4JaAd>Y^43C;22(rX+O?Hf@rYS9<)*ObNqjoR6QXrTSK`TasD>Us|yXj23cG2T%&9%<$@s6~XSh}5tYJ#iaf zd3TIpIw4s}f2BSE()Z;W$4Hz4LSZYFqi-)8ySS06SAK+7(sKJ8Qc~(!&}OAAL*52D zt?*vtgI(e!e8`NSV`QfB2gqzQl!_W0!E_X(2KxpCW4--LB?E3-{RQJst%r)~7fstU zFV=3?lJ3&tB2}CpP`o%&$EEfSn$|H`->71qjrKV z35M!7yR>*iXxpWYH-v9?X+!&mZg)7dlNa=Vm(;tp(b>3B-k#U5v?oiMyBA+Am)XeC zRBkU7+AJnQP%#tF6QEMO9(2-Vf zDDzzcTv02zdk_j=uNJ31K#Krrz6V>i_}fGGvyp`14|0;va@MzBgzA^pNA=rtW3ab3 z`pPg%$0i8YP^90rYH?TE@Io!e?AIvua>zlKAr{JHT5Z4Mmq8=*ubm(@7yl_QX3fZo>{#3VUem2GhrWJ@AJKak=~xA5e{*j zqW{ZYZOD~Q^l=+?(QcOX`@L3&AGS{$rAIZ`ZdwxZzVI;1H!uP3!co_0zp+wm0XD+M zG?)U*blJ}w+h*A~gcEN{!Mip6&|X_y;1ZA8M4qf&uRa81=`B}iF*^vkQ~tZ=3Vi_R zYFB4v($zGSaas4gsBNxrP)CGS7+Iz#$TD57u8zl8j`a?QEjG}L2fNtxuy6_w=fpkw zPM*m@a2_b?zyV^>4+SH^STGq#?SzrPO&QD7aW0QX<>xHQl+$P;Wc%E1;oORHzwmr{ zt{a*K1D&2~x02OG=vpp^^ExU0fd4x6>BDx}1`hv)xXW`^`Dglk zl|DY<_9*BUPS>B@ZW}oMNO+#xx!i5{QPL$&Pl0=hd3ZEd%i?pT|3?w`>)&p-4Gv^h zxC*@G752*N3cqUYQRt~M$GX}hGC40WhV@Z@9*qPv{Sq!QT^ReIcWtNq?;Du!oG7cE_$Y$oX10j>Yge^quy zk(stgJ+6$_v5K9fk3XQ@)VBYOwp<&N+W5uT7kP#A;XfswSb9>PZsx9;%$8xslSDpQ z8&_Cc?E%f-BfR5+(m&m6n;91QfPTl@+A!_!(80I0+iW$~_F`PdcjnGx$M+^@oP+sn zIiYVuH8$i>>NVNkc6g61M$l3JgYOR{H}})BkFo~S5Bh;gq`6}4wx_EfcYZxfVq__Zm5n}ypEAEcqN9%XU4qpUv$HI}LOj&raB`3fWEjb9hxb_WKKO4y(q4uqjHUf0;26v70?v~eKRKbko3}ZyD3DPQgcBzN{G`AOK{9gbWOiyFC6Gd(9{wXpM-IX< z>6TMC3uK^|AJlFI($R$f14w3rrYN;>s;PeoJ_*)gFZ}%N)C6)fTsjRP3vUC*1KiCH zgq}Y)GfRUn+`;g}=psCCx~VUOKgl3-^q;{wnP$eqcgM{^}&JC-%Kx7a6z9k=rqh{j=``+-KvuXcF2;>oX9Gpj92Y&=wkWa!B?!*yt8m#3} zSY#WV1jLbWJlmVAkQ3lv0qGNuz(;`;;u!qWU1p($?R?Qj^jQG}PA?mGBb*DQx;V+K zDlFLx?*NiOxMx1qT0j%P2f;Mt4)`>PMGnF@7npv$@IfF2>VSimURV`c^S=nyKr&bd zhb`a}Fpg~SQ$_DCd&soHTh3tSUN|ic=?1gVx zOidT^?0~m|WaJ%-8UNFkn8QYRsGH1)NQ6%>Wle@0ggLIQQjmo&0jVM3*fLWV{_q#f zj@WdPDp9cel6u3SJe7XAvzND)qaz|;%7K@DjG zWe6{TGy?7Lh?R6Gmjpn z{2Y*K0&+74;HkCd-DknowbuM!Ltv{!V6+210i>!=!ZX$|EXm9PKLn(pweW5rHdzm| z0Qr^Kd|$zXA3@Jh%!1E}jE(TZwXBrbax8%l1F50TwT%DJUZn8c#~BqE2`>XR$X?j~ zgqa`lR*pQ9kim*HpE;|G~vQ&QW3CqOB7 zAbc$WhZ6{EZB6VIXZOd<;lO z6YktW(_k}gC(9`y`XD@H7tMh_0e%HY8#lmVyLrzdKH-mBsWIdr{Oi|g|4styUT4Aq zGAr>zR_Zd4QVYM)X3l5_;IZvE#Xbf82uL-agrC`OX4VSFy`fk_Giu-yz!)9y4{uV7 z==tR=9ka=5p)LYDSL}b7Ykbztew*gs7x5Gy}c<`Y=27d>P>V@MDo7FxD zUJpvJ5#DR*_rXVj*eAYC2M2*iFp~Y|{XilPzysf*C!EPIJ=!srfUckMC3j<*&X&?!Nv7I+=(2CstOfM>vC;Mb#hs)E&EIj96~aL;IJb{@hkP<0DU3*6vdPy`$x7t8>+ z1G!!vi?eY&+~64a2~Y`2K@i<)cqW(xItUlQiQp1)16Z0uWq^6Wo>IbHA`AyNfWO|x zBnMs?&u0U$22=qzm|FyrH?FxuvGu!JQePR0(FMyasDZBpAXWA#a<4f%w8@H0CJI~l|8?G{#&nVup w&B4uQH+#3#ZK>a~VN1i7mMti~tQ7TSXSC^&!L5s#oW&mr| zi6@a9wo|catGD)7+Im}CYsG32NVPKr^8keaKA>6~)Y>zS)_@g)59a?}d!Lz15PN&? z?eG5j`Tu_C!a##Wyy2mNwpcL*v)J;kou}OP1Uk_I&+%Ph(_>=f)+Tz-6;M-?;U<>(4GJDJr)B z@BH0wiXZ&r8w)bO?Z4eK|3`S={@XvzUoD<{=a0m5{%`iqZxhcw^M4?of13Y@NdM#f z@8S92AOE-@f#>KaUYY-W@qAzc@FWXmYP;2rOc@W7&u+Z#YvHfiY@a-gSgPRP@H`dIoPR!>&3CrUW?MTR z3B5=!!t-}u$QM6*smv(a*pKv4$k+LWe7*%U1OC~5o9zK4+uUdfwPnv2^PSz;)OanD z1nodWk?#-3%6I)Ow<06al4xpk;rYfF^3@^s|5yK*ado+i>9@IT8vjH1a*Z3^5C^Q? zYUB}(+Zn&y#nM}Y%6hjN^VwP2?`8&~47=94Ei&EDH>-(#g?isz+VF185aoj{yKXy? zweE_J~zqrQ%iF}ym?x|*pj`X^(>Uled<3iP5t$BRS$vF5{3 z6lf~gKz-AJSDR0~qRZr~%|odKSfqkG@JgK9!Gv(5j%D6%L^=l}iM3@U^}m&qm_1*6l4w{9_%jPoueGlXNRc>x16ji=A5c5x2v`87> zf>+32YS!*P=Iw1PV(EnH?OyhNDqoBF(<<*)jT_5E2F736-KQGp3&i=|8+<6FR>u7s z2q(n282`>{1k>M(;xFR^tTi4P1JYjq0QJQszSy*4X53zpny) zD^fVc^`Lu=8EEO%EoMq#hkeq$yfO2_=!04_%9wJbmAKS zCJR=l6CacWt3O~ypuFbLEA*(+|BT97%Be3I-^+M24Qb|R_5fMqSLKf(UIVFp0poY& z>nM}g?^0I!(b?wsg*IDjTxtCZUKn3P1vSp8pc=~`$-MQ*ScWu>@A3;eFIvs`Hgm;v zg0gwpd3ez(H=B#`t~%mOAIM)enlj9l5wc9mEx0WZ^L?IjiXby|m#At&$njR|2Vr;c z#wq0wUU$KQ42soh-+2eCcZFJe!sT_!`utOUlRJIN`t3@vo29$(qO9MqD!~Dk-mH3e zHog&RJs6(PICm4~j;+i)!?pZU7JI-V$~N=e8)QR6U~7%*ln+#_?g_QV!xPc+6^H=K z1#C8|?i4T@Q=ZIYv6(K-VS?uxO^Yt8eQnQ_uLTuV?_s6YiDHaDE(p%+U99p2<{fAn zQCD;R=@E#CF1$4!VBU6Wt#WrS5noTcl)F1A6w^u3U|w6~0;b=-6o9scPug?^K$?GP zkm&v|%FtZDm?Be3^)B<$OJqVTCGa&a@yKI*I;0OH3B+Q&pSque<%LYbFe_q49k9^{ z(372YeR;mN#(m7V$z;a2`We@2s1e|TmVH#vJD})$1O%RKmSYFg%{+`}W#w~}WQYbO z|AvBGG8j5QY1u`sl3Zl<1(SWDtbCS|ys|$U%W`{0TO?cbw5)R;V5iDIxDK8+a zZ3aK{LI%bjuf;SP?`Y39v+g=7h5^fXzeeLXRYlUd)yj$a)U4e2B)l3y7r%6f(3g-0hXsW~8ujoZNl@Z%I zwr5-aA;B<24XNWmIzcfS*ATwl*>S7qND9VhxMsxiF$W}+mK2KD(+E=5KTy6FFBX4H z-pmgu!F~*h$(w~BVHmw2(CFZe-!8Z34*irXz@?nhNJ7%cu0=~}#7>Xpg9#H&ll-Wm z94xjJkr=OIPQ_sDW|ccxRx)eKlS$3-9!<|`dJFI-gp0b|LRl#mxS+ zYz_(JAwm#h4|Nui99%xeYDosWWa^W28bV#)GS9!jKi_|i|LO(vueqw@3-UX_f1DBE zH`=w`<8NFSBoGhXbN$jE6I<;ur{yw zs_gS(yjYE3vATRI!ao*LZ>xV3=p{JhSqM53;N?NS85~10{82<@WH|8KX0DMDLq#^@ z)v9BEu=SnrNM3N`0CgO2Sqrc!hqAsPpsc?HQgex2S>LU!585Utc&`%J8JN5|GNL*? zIpI?t=wf5qFl?*#6Tp`2x6!yrl z17TouNuK67jFN8oiq)fdb;(x(L>W*d(;v!s9|`#U$P~r}Phk{eQs|^fVXQJf3e;_Z zn(#U4?BuZDnjE0p(8QqM3Dy*ievX>Sr98;Cou+c;y;sa?L#1fJ*CZ86#Du`56n!mM zigtZbie7C=QR7M~90^gce_-*^79aw0E~E$K(YZ|jq(IS^h}dI_%~_OWiA^!Rw!WjZ zm_V3IL4d}P_zV!kSrW9JBxsK8JX6Td%Xs-YAuyPfohpBs&?>;%^FJ#)HKQD|Q&u8n z=Q$bKIm;R%UzD9s)JZ~aA=LCEXtPcst0#^UmWwIrKNObMSj|j~GhA|DLRwyf@)#x# zIWPlUj&}d#PAn}t14=NCfq*v}2{hzJ+!(Zp5ho>q1~n4$@q_RM0Ep3%XgU+X(mCp> z@}y28GNe3tSXmzeCw3FRW`swq4(jp?mTW|g{Yl^FkdJ>yEooqqI6o&o%vDRCohlQ) zBe?dzsFnnTTGA-h5=%;t%Sh=lmXyvANi^Q-MU8LG$hzevnl*bQM|}D(BA+#q&%d25 zDMV_wg?0vKW#t)&L?Z^}^Klm=*HHPKkEkK?`J;bzK zM=9gKL_RBbCy2F$@-B4QzcWhC2+9`EIa+zYf3i%N`rlFBhl~)sI9LO zLRo6-+es48eEOf9S{}D%gq5Aa_AF|$oLatX4S|aUpdnKWT44gcA!2q=BY%mK ztf|F?Fl6CBacX&d-v7|lGINUT{)?zN`VqAGb0IEsrqe+^{4GlQ52luk9DxF!ol^dU za{tXKB_l!qsZ&d9j_Up25bAr6t#s#D#Qx{?Zz11bxm0%UKPKNVtVQn*mG9+<8Y16E z%ZPt<`F`&l+3^2+S zjv?RCivMHsecR0cA^G0;71{lkd`FxApM005m494m|Cg2TA6rYGe@wp5t^R_14uwlVvWL?^bOVK0v!9=Ytp;CXS1&+H%jV-1d{^4r{$p3N^`qlZT~ z_WO^-{n3G8jjsjId%Ybt3KaC&77gw{`!~`CcrZ505uCi&zs{~H_1kjcE9-ZQ5}Eqi z8XQM91D`;f55I;y=IDThTlMxV`!lKvOzv3rlK)7DKlVU zHus_~U!L7+RQ|H}GFZM0O1uX%KDHfPa^Eq;>` zynpa3b-UTMo3+Y4`X_GX-p$AiTdbNe^Zh)2XiuUq-_c7A!QM)qqZ@zg>;XrQ4CQ4* zj%+AD8**kt1=&zxHss2NhGj!V+0gK8s5l!c$%aN`LkdEwzS-^Q76H?)w9ZPW)38jB zB!t!UW|4w@_sEAEz8Niy9@>|9Pq}fk0v|3%w}|n}s9h8laBK~=zOJBPdmk4LWR#R>%GAQFJOA72^9D|Ag_aykBkI zt+dd7IX0;@@8%{Yw$u*Kk5Fre(()ulb54ySSAlahzJ^*5;`wk37-+=)M_D3Si2WFU8~Udwl8jzgKIty45{7N7!foc)VVy@6^O@dG zEw(K?6FsvBA;MdBoiU{wL5f2g*`cQ?A1r4gf^*rUJ>lb6VxMd*I|Mq*#u`(4{kE?V z2hxsp^^4{#_;8>feg64kUo+E*Dua_QjfiF0@jP&X;b2|$ZUTfv z1G{+Ght6ll{4(;}sX+_^9kPB=b%s2d_GbK>NMv{fyL%YlSSDlx#%wxVW668Q4>5ih zm}IOql7|df@xu74*2r#6M|OY(8FlNihPT*IJ66JqFDnuMvV^u{CTOvzA45kyM-!#= zmr?me)STMP{Kq(k-SV~f;0=JAoFiZVlkjMW z9us#Ym|vFAM!OToB3$>ECAerK=NKh)YoX+$g5J&HlV~D96RgJfW*WanWH<(ZH7h{i zV?kciM%;6(5=*}_M&58#x zKH&A&{Qg?%3+{a2d)5rlQtlQp5c@Q#MN0h}`oU!k9yA^n!zeQM!_=5lp}7z5j2A-O zMnRXj^ll&*F=IvpBJ+_r4Ffk0UlWR(hgUZVkzvf3wR8jdRAC%1X0ehAY!xCAVyJ!v zo}O$O9u*jroGvp|SP4Or30%&#)2c9UlGNeTY7Ct`Js97txTpD)k@{M^DelV}l#zU$ z2u@pw*tm>cir8KeTp6XuNWD+I2@w`BL}6o1TqJR#M3=|_=CHA;B$&b{Fh@M{5d_-0 zn@J*K?%f6d(B!S?Db5KKo|w4MX0wN{*9Sb|8Tx=PeC||4+rysmXU7^iTo7Yekc(I9}3u)4M}_xiGY3gH>pCsMwTD*H_+VBE9k9uO$9n5 zq99O3fdb8_pO>n{%rI{?+_k8oyCMex;=>_u?;*J4l~liwUAuBETBb~TrhGLZ1e8fF z3Ueg^z9_*6I_lWznZX}^?j0-p-!ujE@5(twZi3A|?TQ7THFzZLDSm3`HEGhj?4HM$CjEF&@PH5s(M-Am*OChQu@==0e0Y?;|j9hqf)WDXmu_ z2DrY7`m5uEEunY3xn*jfe6w2emVC>!aMak?Xgs$6pFP_L^6%h=9tWQ%lcs_2`Blsoxb*r9vC__~G$4UwAtZ-Ia zg(TC;_d(KLAkdIZTj9J=zRAkn&KkUCNSnHaupSHgRL6nf-)(EQOwB(+#_D*=4gPj4 zw)mTbCB|T8>{n!@K}AMBRAeMZMMj2HNU1`FRto}FNP)rly>j+~-h3kYZtzFSQ#?>?jBH!CATk zwE6vY7H#NcLTWcDwSN!4y0Rdl)(9Q`52Vg}I~AQo3gaqp>`_`y!b=d#nR1$Tu}Upu zMqYJ1`5J^2tL$gq{XiTmkiRA$rx^ZMR%tSC5|K$1P+A`pv2`>b!%Mriv7!mS_F{g- za!ac2(9%7~WlK4!it8BP-n=i39te9F?_`@O3IC$3GQpfSA1d)4V9GT3p!GAOsX{Ii zvM^sATrOZqTcQeTT;_u`^vLyhBLx}O27HD`<1aAB3@ihVGdEz1Ni1Xd$IQ`*JY~%O zY#-(iW?znuSdNakP^5e7-A$uF=egGsou|nYAgPlA#&~T)&wj!&{19WWrX_Q z=`gY87_?W+t$rOxRy3Yq&rw1AJHl$e>Wa+3!ec3>$^aZa`TdI#(!8%JE#&E|wZ5dZ zZbbk_lu;^Az>zA^SE%p13kH<{@4@+%mR&1$hKw1mkg=LdBALL23P?(z-_0J(BT`oVpXx z0Eh2E)w@*VD!0lvhYV3Z)VfP)A$Uw5u!ln`kM^PN>0ImWSM4+0lCNUUbH8rKnGc_D z109zk`k|9H+2mG@CU^3?1V83{e`rQD`6x(_|J@A0jatmSU6E4& z?cKkdSy>P3#!eNc70uC$lBQ@?aj$C3aI0wnNYytGok7VN?qz3!>U;23y~`qjW)ycL zNO5G`IJA!Ps>W)HK;B-8hk?Xe_@kq-@VDwgQB;X~ylvEFji=B8m*nKN;L&4JQRS;2 zBX0F>Z5pOJVBPX=T|NepR9tn;a;e_cE(DilA)WhWkVI8dCdn=W$^<3%NQ`gR9AP-n zxF16rR)cT3$OLhymzD|MF6C}=nZ;?98XRk>P*yI*o3b7!9^f0P8P}CFI{MO}VHG}4 zPui81Vx;Sb5qObkLcYCTPOVY&8xV0&_aU4I;SqDzgNs;KoeM;_b>iF;sYKgJC3+VT z8gF7De&<4!FPR6QN|j&ARNfWhr?K58NvlHUFhdjWY*6((t8C!{)w{$)*1#o;AqG~E zq#_Oc$6yKp_B8@~p8)%U%3sA|Dvg6h;p3<7MndE^WW9)xAq;5JO5Ff>ONk2#0CK5X zdr3w36u{Hr=t@0R5yodqD#C4xk$5i0*otxU5?cS79V ziP1tLvWvy?YlWDE;51jmTL820V_1pzgU(diu`>fe7O01tb^?VIAOX3bR$9If{9*gc z*cV{!^|$4NL1)4`{vpi0m``R4tD$a!b(mN0$0O_oxH3AR=fLTRE^|FYQd;J)-|nf7 zqweH5QEZJU7R#S(6aj8wQ?CNM9dmGOvPw0!^XK;x%)Ekwnpa_I3GZYHB4|UBUAFgANsWfrghoduG7-JY#x|r6yTa}jegm%n% z@6{G(%EOF+nu99m)IdEsI(!C3U)Yb47oJC>z6vEbFR)5x#B`>8G0`OfzTLlJj3C%H zbJ-zb`}`5ip^MQpk%oN@G-{jqRhgzE?XgVSSS#%!k*2g>i>^K>R_88a4Ip5Vx}Dn7 zr4RN)yQF=)q<#BQv>y}@RwKsEw;DmBA*bbF@i-u}+F2EfE~XT&`>4Axu8(C65}Ent zi9&ZdnUM*Z{zYNDUgl(cXR3(7lHI)7=A%XnW0Ix37_%y}26?N7h{vLF$vs&2!1Q5R z<#TvQ7%-uyRO&*Oi`E!AjY=txvwh+G6kh6C(Ua3c!QE0nv`%pnOM$yd;@0HY??jKm z%uG56xGgd^+U`cs7Ac9gS5QD68)7jL9laQ!@}#Yh&TF*k1P~!gDU(6yD|!`z(Ti+hXw0t>%XA$ic2Zi;1k@-d5Ohgw z4Hc!tUs{O@C0a6v05Ri!o!fZ1!<`}k+4oCJcfrz=%9q_B}3+iPdg;In@(v* zpd`;KGv9tYo%ZUFQXK^6SCk$0{ajv`gW1p}%j;39!N5MLC5XgwsYP%QW}eK1P$PK@ zyyTNscZ(6MhuojiH}=!K18xY@=FnxRe293ws)JoCgrAT@wCJhP2adz>Lf{W$%ktl( z)6WlptgyH&BrBg|YkHp+)90Zz{*Ay7nbtJmh5BpL0ogD2qinRDVqcAXBiepB0v(a- zqU|#g*b=#_MxTX%p!WdUl)MtzB{3wXVp*lMG!c98;mZ1vEDc?p%u8!(P>@6*9l>MJ z{##!S;|!y7AAuPqFm}c$T#y9`c?)HN%0bn5Mo_|nKl&K(7Y>Clu|gd5-1kUe(%2kK&OrfmtSAyCJF#8&!^|3Ui5FG-gT$PEVJD<9g;biRKjxZpvQ!fen+z_85rb2X`n8p<#eHT47cV& z9Ltk^nZ~UMLeb84CH5w%3AC`*_lQtEW)e%W5zQS(&HWk34&9K{5X!H$c8URU3Z<<= z8Z;QA{V`F#eVu$fMESVrGur1J2hz}*@zPlS3&0dBDDwv}#Gn{0m2x(OMD;U=K} z1X0+#7vY~~!Um^F9vnhF0u7&)dmO#yMiJVI z(31$I<9@zP-ARLWlMUS#o0B`6}tb zR+nuvk63f_$EaH}r9FHB)E|byDEi{x3D$O^Bws@uu5yOO=4LFTVSAXTa$f@-5H)-Y zsgqWJoK8=rC6dzms3?L9HS9Ao3Q3k%GTeVIXu{BZpoy(LYhxByb-)sWg?q1i4Vkuu|M3NGcRsF|q0m0+1L<$UF~k8(sz`lC(nV$wtFJizF-Yv0T?=VTknA z|Asa2A^fJpEpzx~Fq7c79>0V5UDW^&L;UJ5x7o^P+H5!C_ZR$Jvuw5{_`Q!`3GBOP z<98W;)2|r(eHGyj{J!9Cfg3?^)VfGmKlY&0MBru$zzov*2?ADsC@mu?wq4wUBq^-7 zlN#sX34>E?1WHIdL%;?n8puF|h{W`$#Jwv%pK37lMuHi90Oh19Et78z<)chk$Y^ki zQWHrWpT^U>Q&Z}<`t=P_0JenyB78Ej0Xlyv!>uk3-wL(T=hM91s#2E#gc_tk*TGB( z%f`8xg5i^nDg-UsgDM;IVRCS8Kwg`npM;1g_KUDB&*zq@l?^`hHQjq7_J-~H9t2Yp zz>5T%Ip&t7@)@65hD`CnOnnGqWKkB4@6a6BWCn9icwG$Qmd8*LEtAB^u$EB@_6q^D zmng;Sr0Wot72C`=-ypq#T!+GEphYqRF7x2Hq~mz;KNl-0z29kkay9w8oF7 zDlK;-L?EL{2WIkwKi-$`kH$yiWd3J?wrP2o14k$;AEfM2(-F)|h)B}pnh_VBY{x>5 znvT09mN%;U;ezESMGrfd9~a-3mq#8@(d2b`@w{$-UQgR}M+%0s!}*cv(ZdCeLF9-g z&(7}3((<_%gdqk2m}t>Bf{@wG}FK3&W{wsR5ZoW=sdHytqzKX zXp~Sb-vdJE5r?k@mzMk#FG$>z+chUr*VLmwW!ESzoUA;CbuY@$?vZfOKIj(uX3S&U zCPc^4_pd}RrW~=kU!H??=|p$>C)eP)90Zcr!GUd?c@nZfc7g<=LH}{Z&}|6p7eUGe zo5tS|9(iP-zkmrHH;_x^H^GcQ9rh>Yf`w{Q$eUP>m)>N(7(X!nsUizwbBgn zEvj*o$c0qR@q#wV=bjNOwrh4@8JbM@^rSA+Vk@aRs`rDYQ-O1ZNC0CQBO%1F*|OB7 z8Rv!!*mo~=rC>;B6?v1Z~)x;DvC9mg8i5PBx} zo6Ochi^8f?o=<2!~9|A9(K<_GhJ&Wvp3n7stj#zi247 zN^FrcaIM`^TewW#F{NYOi@L7MUgR(wi=n@MDiF8~(!~Y-B<9FrjAPs_Rp(OH2jHlT zOQz%nQ*CwoU|p}n(n#De)!2_)rUtC}@L1QL`L38Xhb}u~{C&$O`0k}vX9Wj%Gcj%T z>l1A)gv@>;*oT{~7)_T&fmjp7#Jrm2jsXA2|D0Ht!H%>-GafCEq7>b8(S|3hy|PMa z{WpjQR{Jn50$A+%ZjfB#KlX?w9;ZB*AQ#+x^wR)t9zhtF&;aMbiF&(v0UUBNv%$So z%qT<#jFTcPl3yj?Ug7wdTO=>1$d#EQV)8(dj$;nYnn~lv5&+DIxF*v`rC3==zmgaARHtP|#w*1GtbYm!FAPU+`DHe*aw| z{t|gC^X)L0n$Pq~#p6SQAweNp#@}`=_A}}hm5(VGy-|BjANqq( zR|1EHvvnCjo4>*k$#jH+@*DY(HG)Py3;iHnMkb}*CCtJfA`~9C0p$M@f9qWQap{ct zw;dAybEq45E*#&G^B(^wyhkl~eYrjb-JbF3JW9;b99j>-5FJLcu4Z!=$if(W()E54lwseC)ud0is31EJ>-O2>&v zj$R(@!#eK79xVQ_L?2CSKipgf?-9rL!SgWxXnUX8ZuLI~o(jf54H^2Qx4Vj?YT`pU zy!XK2Jp{LAGn(mu9&zQ*cC*O>@M{5J40W!p>ggTtOhw=$#fS!Tc zAiKzZg}Q0nWa$h1QGX}Z|JQ8&2ai$zM=!|+oSoYMAMuY*;Lo!7W1I3A<)607{}nPQ zO8`+`S>Hk9CuLO+FL-Pwp!r&55<|#pe%LDhB#PGt`;?YP5jb=Yv9%40&oDF$nx@B^ zpKimH-<>Mp-!Z`oDxXrW#qQ|iiMG(&3A4=qJTlwbf`~|%?zZ4Qdb-+zZ)NsV#loj{ znkxdO7keYy%^4V%@`6mIk$uzKn@z4NG?w=ix2r-NKubF?@y|G5K>X+l5B7;wJzm8OQl;?!NNjl^A!U!CO;`}p zte!oqAhj7ee|yBN%{NM!g$27G%vEyeHT^Gk8rqd4544!k08Z3+vl|Z_`8f1c83GW6 z<}ZFJITgO8Hu%n;inXbSvc~vBBF8M~C)-Tj$~A-HpdVGyEuw^ubm^B@gu;Po&umTh?W9m8#OxgV;wkS&-~2Fy)e)7+HK zj9Xo58opAuqJ}#?%(#f@oi0^BlCRu*8$=T0v*4Mn3suhmWSG`{*ng2%W+DP_X5w>d zcjQLYOHRegdfJ^4rd|Z07Yp-zdQAn*`2;}P=G(WzgJQCUEBN7BoYyDThs8$v=BI`u zHbk+AqS?$?;$d+1GVIp6n}`NwtV&!^NIlEI(3yK&sq=!>yEI(B+10!nL?Ak7#{C0? z1v;}rGhlQ;COm5pUk2jh;gb)tkx*t7f}Z*q-k6bj(Co-~Lm3KU;EPxUF)21viWRhq zh`h%AG|MpK{$5JwbtpNLN>Z$qU8G1LGH^RZ(Y1)+u8}os?n$p;(|v zF{n&TNXGqrluozq$vP?4$}Uo5oicAT#n1t+3Y>=u@q`+?ye!1kOr>iD+&o}IA>@m^ zo^zy3>^CmN!gay;8d#HYwGC}1k3*&| zod8jnvsy3i4JA1ed%BHUz{hI*AlV;USI}#1dlU~vI6=ce*+6Fq6`gGG(oeyyvaIdMNT)^ddSR%VbK1A43BV}07!f0P8 zLAh{Y9I`sF2|;KtA{@Cc67AxiOoECP8oZF!xX={J`P8$EV&o@MV&39K`QnQ#yaxHNKHXqk<-4rcg=4IL6q zn)L|7AATe|hlou(G4#MO4f!Tm9_I%|q#uH)-HPdz>5{QUf(YWcolJ-imJmUBu_GpR zGVby9*dhy15xqm_1Wb`cHs>=XW{?9&fuw>_nqz*j*ViEIA=wZYPASQki5EGjX|H!% zMrPtF+NVsCSOrIa4;-ERsmQm@G_YvHCTUeJ^R*MtNwh2^JakL;#kEoz`eVN06Nw}DAwSD?8Ze< zb+CshQkpOsjn*pnYu+Z8rqnmNFl&Z*rx`|ZloJK82OM&AVv|r5@g8U@#g06*P_^<^ z22W;V4ONmFt$FJkno2UmrR5$$SgU3Sj*wsq7K036Y4iBY_Ww>LO#h?(KbgsfdN;g6 zLZnOFj4QYd_BBLH;v-;79Vd3Mg!jaoQzhXn$sdV*WgDwqUi73E8%0mD)+$7h7)vsa4NvWH**1+P7_~iFGK|6 z&-oA^;sYB0Z399Ye@o*}%tMIph1wVk^~oc9I9p);XuMD*V=>y&kmv18jySBy;NU05 z43^SQAi!G}1A@_7MpmQO!)L-lEh_Kk7he2z(2@iY^y)Gkl#b)Md;lnkU540$1fQxl zKkot+_t2C={M+} zn50((aDEyouszK##}T{WYG&LKrI>|euEg=W;96#c+VEV5=W0BmxnpkH91f`IcnGFg z9G~kKFyUghYy_5w0boM7_Y&>^EpOz6OTC6uXd^>(n>3D|<1pHXbc3;VVW(yUA5wR} zB><`Et(tMgTB_t^0)NoIk#NQyR|b9v+o|52%Lg{td=MJJUP`8|E!^U*)OE|PPi+G9 zN3DD@Ky{+N+lhT?88LB_XxrB0VH<2DL^MKV~4DNh`(j-sITQ3xCk)v2=7 zY@qZp;%Th~+jbaWvHMwtB+T97R^jDzaIS(PmVE~zZ{85NgU&a|faZeX<@o>TTuV}s z=Z0`y&MXDRQ_x%EDg)P5_oznOJP0m47RyJ2d@RI+92-jDY+r-0(xRXmxb%A{!Q0zZ z!YXkGg1<~zc@Wk34@HIHzz4#OQ)AJ7kqf3^lo+5eXE2|${!}4O3(#zac$(`}K99C( za5#Zg?!iWD8RD?zvs&Wx5aAR#SM}}!>+Z6m{rbizGTFilRWw57ez}M1Ujc-ex1&i- zfqk#^sovd<3y`A*qW^dra5KUBY9-vtam9mbM5zXm4_UCk!gz=3*v7ouFoR(WI7&79 zyP_3%pxx+ioE)ga3m!FiQ183I>-Z~%rInR*S1U(`y`_Am^{|@;Qd#b&H>&iFq5|@1 z<+m}Vl}tg{G@w4fGK%Wc_)|nDA~wGalas0PyBf6018nyPY{CHZuAz1)dI}Y1>~nkv z*^3}PE@n7Hz+%BP_!>u)Utn}r03gBz%zKF^T#j`o=5TRbKxt_~)pX*BHd@5#09=4V z*w8_MQyItnx@0M%g8;{6&H;?basSW5h2#8^3Rp~_cW}Z<7<)LigieEChsR5Y$gmN; zD_o;-2drGsz{t>nc2&V(W~&L*rX#1p$S+!--hnYGN~gCYFL@|NUcm`9)yr?4jw@t%^=3JhS$?kXKMkxEDp@A9R~t`KL?prO=Gs|Zim ze;-B1*dqR}l@vg=w$ugU(1o=w)7zY?g|M;c*n$f{Xx~RajBQKW zo(opL&SJNLhq{)3%gZB&v`43)aSgpBNx!y^w^jm zmI1f`rae3+GTzEl=PfRZjG`A+@4&}G;(o6-GC$l5Loi+4nx=7iB1*fksR?fqe@(GF zG91~&7g#nozE7V?i4}tlIW;^KE6zuIaWRbdCFSl~SP@ghExL-2P;hbkZ_eV>E55hU zG=gkWj}wGslCYFKhUHWhqF|Y_38Hpul|4B3sKw4Nf+KhyO$!=atNN*8uZ_O=v1Wks z*M7@vtp36l78&ux!V4A}?4Z)lmH0dgCkqYA;}cEz*a*rCT&VwPX*Nvrav#D?g_jgj9A|u*a^hO~rFe*U3Sd1$-{T;Dl3Zxcc}JM4j>jPie82;@1(g_&S5Qgg=_)+r ze6^4?T<~yM?DJJ+C*!QFq6xxByPCM8s0_}I`bHND=hJOD5MUl$0wt6I;e-F}@~X%% z#-F13iI!ha(afdsT8%#}sp!WVs|)(djM$mu8Gk|bnoY-(jkod@)v-%iUz}ItqR$lI zs+>4B@$jvs${lJYMDUJfV?xGQ_yHgRXGB020B)*f`d{tgYB=_w6?z+@XqSBg1os%I z_`9(2o5m$ryOv{ryF)#M>0~poRa+C`7CKc_MUAeY=6gtJmx+%k6o)ELR+~Sy`OpCP zjijBCQC5u2V{0tHbwTrSq>p#eV{i)f&6?}K0!tx)yb^Qhdq7lgkD@1R@UY=C7$hpq zNH}P@g4M3{fPRrttSb*JF2Ckc<-cYDxlWUN*jyOd6U^~_BAAMeY@8=ssNzFZV;Cf4 ztk?%w4djyx_qYk&jElo(jDMeiLIcDnQw&i7ZC9ax8gRUZ`ez<#9aM{kVnQJ2=Ln*AU6XOK7r9eO2H;EX*7X@Ac&xGw9G?xAqjUw zgP|j3N-Ld=XWk|;X<&=C36ij$CJjlV$cQeTXoXC+l$bQ?A=W@Cz5p{7o+uf1U^3ZwI~#!0)5x1Y$wV1uIE2RQWD#!uJ;boC;K+4crJ(|vvDH_kUI#2Bi;=(Wp zu#4qsyvgziUNx3?`zFyufK$9Q5s;V0eWK_2O2IGiFqweWE~Zw8Lp6dJ`W~G+Nr=UW`HTHnKI&WMiCX?S@C&e6Clppw%LSetmV^6;%v)E~g7XAunvc^U|xfncJ}z^R=WS7n@^X z$z^;G(`#%{1$no5B9c&P83yzwDuA6_h#`%aGy<4Ey)*43KX^f-lyD#s3m~yqk-dqh zV z`3iuZhhlWddb@c&(r{fBxXSE=^gzYBtT;R(lP6tjJ}K6#PCf02__8&gol|3Y5=yET z%EMyw?C622)R;`(bm_kXgv23LQ2jck(whBhSn)ERKbd}`sYKqXrt$&0e_=lcNyKTk zgE?^I75AGFgJhXN#|~BUKA?{BnL7mcR`;Y!E1w`n+mdmd_0W$Exr0txH9wQSDd|>0y zw47U!0j-}v(T4K@IDS)3AHokIrOUYlF;r`pb1_2Hm0iw8D$#jhXYx$l`BqoZIvoet=xcApUoP;^rRYSs$&(Q0Oe zY^MGb(M%T*t>WFHoj0R;vyt*660@UoCbbLgtgzbo7U9R8UWzuHzk)Y-m-D;$E8BK2 zLb7e&vqBkkscqju9G^`_oiTR2o=zBFO8h2pqwy;CihWS=(xsO_PDr#55H8qU!K|_a zb8NcQPx0s+cxzu*UV}99s{_;q4Ni+_A2u#=E?bj!vI?@b0c^T-lmJJbW?(g;Qdwv3 zWo>W&l4^UJYO7Au4OpmhA$TlZx|32+A9TP>eT%8Sg@g5VQhiIQ^1=Fkf?`-Irt`l2 zl&Ddjm=?%jnMU4V9*{ANr59^I?)}dNY+ftN7Mkm^Bau4Zd=|%^F=c!vW}tBXdGS=C`6G|b2LDDXO7&sl!`=i9vye}|a@ zQgy8UsXa1Mh)u>{f(D;1{Wa)EEHh8+hDfP3#9J`=hS;qIot6tAxHmFZV$u*pgOcM< z0WNRBvjotc=5x};B->6WFcsPUhS^WbK4rc0rzqUo(@tYi33RkM*Wle;<^Z6t357sV zV2nBMR6&7Zpx48QPrbJaHQbtJ$yE(T)jxS)p-RGK<`d}q}=5ki45Sg6Y!u{dg65g9=HIR1frlg zP}m4k*vwDxN60d$28j)!ufljz>%3q&ZjM;Nt}R)N02Ggj^l)r#uBGpJM;2@t4(Yk5 z)?XHxO?N9i>&GtN<=C_7MX&(hvW;r7Vk}n1l*3jXF5}aSHC&il1V0xCOyTiHOaz-N zQh%!6g{7UOlm4x8+a1I+Mc~T7WMRE5-MgM?_EL^KCCp5}y|~U%3m_!p~ykegMk>i%oq1 zkJKn=nnZwtbZP$22~n7cynkv;O8o+Th=n+f{Y=?gW!c3?mF*@9AEWGMFff&OOA{;@ z#$6q+?oob%%^#S-0@x31?q3qV{uY1p{%O}Qi3IpA-e2A0kJa0QI1Bd^fAfJGmt0Sd zYQf=K0Vkgy2IXDY7b=Etwq7zVfZO#Z_FtSPpE7kRJx*}0JNy~+ zNzd|fh6R$Go*B0Gn#KgUa|6t*e(WCy4CfOFrV0`FC;}TG46qtypL+YheH!74+5?<4 zw){-p%BHu6H5CPiCs6jADAMJ88s{CT_X0-AIs}zXe;K3b_sfVMAfg?;ZZpO`Ak$84 zx(zu_YH9*_doONY_ORi-wAJ3cT6Y%b#y-+|Z@0-#WDbY(#M5@miHxdu(*$P?+&<>Nn z#Vz!K>Ugd57TiRGDMPFsF&w`j^K(93?Ssq@k@&~%)&NW~W|SQGn|yCEZOcyZB-{zJUz-~N;? z3H+`M7Td$eAsI(Jq2s8IE%06?qsNLn?hH5IVY{~J8+{SzXj2bAitN~)i*YgzF&Xa6AqC11W2Cm=UbVhhX=Od%dP{B;}F0=9B3MYBJSG2HeB9`_sKZ6s6Lg z7=J+a?m1|NpoJF0KD^4Ws#5vQIEB}Q@Y^|NN-4+FrM@4dFHzNK#=&6S=E4%ZV;zG! zKgdj;q=T4mgG^an<0ko1XxhOhLv@Y&z}5hmQB|lc=41NUX;%55xR#zsk$E$~nB?`E zO_KW6tidR<7~jw~8}defpEcJC&X}d&R+b+Lalhv*k8{oObb$f**Iz~LxR71jkPm~z zNanb_2AA7am}j7yQl8Yw2W@UT)8dC&2Y%c=VYp0U)!~Ew`3BKI-@?Q?BmEW)m7ITr z_K>;c-9TgHqhG=_nz3XKrfYn!vVNDk(Qf^NZEdTn54e?k4giNms~`-lospXS4P_DY9&2^R`2WADia}$OI$i0o~n6E&b}C zEJ>&9c}ug+ooCbNj50(_hd;e<(Q174e7|w|;YG&fpSN9}?x6R-8JB;C`RVdQ6oKbQ z#^s+tcJ`vxnF1MmsNe_geBwu7k639Qd2+y6U_y@8FK>zRGIt|(wwP;K;YJJ}tg+=f zfw*iWl%NMf5Ykv1IL*8w!D(1W$nl4@AuqWe%DsL#9j>N9H^SVC*@~AmkT(+FgL6K4 zE*em;hsn8Xx`4DN?1H*u{+7&6n9zlOE5_((R8E7yiD+2fSeY=Jat#h>Y1__V%r>@~8?Z{y^A{}*Hx+#jXvFXj` z_0ZvY$&bia21YO>!3NeV?0pDB9qH(*UjZcshL|_88JH^JB~8eSmQ_$qXvq6$A&lOo zqyo}akaA5i=zdLY!xZof05MO(v}-ue0|0#>jhiN8rC)*B0D|jvB%r<#UCxIQLG?kq zEi?hzX05`6Y~dvby^`F;COOFHC4a^{@(Pj(-Mm7qm`mmZ%%-(4QJODH&_!uyBjN;D zkUk66>PtRuHd0e`?fnD<(%Yb${{X-4bg2UkOP5@LSoH9pax`S(yg~k)I|m~}N*I+N z_~_B>W{EXkAzx@G1`=i2Z2a{sI~yNTZz`D)%g5Dt-OAl>V4+|M7v&xWF9)5EVo8pV zv&_Wom|Cm2^Xe40me-GgkCCp~`x(BZF<3v|FkXLl6OGqkbg>1M-RpWWev(sJ3G`&bOY>Qwp479PL z4qim1$J@9lpD&8%BR{=ATZi{W0mGNyk$I=`1y&@=7jytFmtczlRzfYd>RUhEg}656 z8m!-{x`XgRIa!42&%k*WYbF5)^)dj;v^OUmILsW8lL?=T8PTD*^G8~88XBAexmwXp z0-NGVu=L-9L`gJ98mb@Z!pb^S*^!ws7b}zOsS=Pl zAZYJaCJhrX2+uy&oQ%poSqj1}q=W`a@X0x}0g*n6tfEK{Xjz3A55>3@ck#%`uMq`I z1bH(AJt3z*X?{&2YyL=@WBUa;U4F$~M4W*^NE0n2z|ppt9KGB_XSk=1hCfn>?^C%6 z)m-H*Y1clSmeRLltG`oec@Is1*i~BJMgYgPm6l!vcm#UvBZvC?AeJ$E497_zrS)MX zq;3xJ-yM3)M!GVV%WMsPZN3gF;`5l>)E1r92<*?AR`OasQ%JzC7^ zMF1_BLMIzfkH$~G~3!X)@Q)krfb!Z%=F6n!G$K;Xhoh+WRa{L~Kqa@CGYM7Np(fbF1H2P_V}Uy%e!DyTt>zkdamB+oH`m-p&#Rkj zkRJ}|%{4Ii#KRXg*T8InPi-~Vtf8l;x#pQXJV!Ry{1#9CTEw>ruyAMNCcv?!&e_yT zkBUvJ=yA%X7J8hpshJ+5H{C;z;!Qd|oSW{(!H@lLfF)lLQ0SSmv5yqggI!5#>xf94kiYnhr+l@tL06nmp9AhE8ZL9fkb0s2j zL_t<&MyK}sL35=QD;~K$z0$b@F<2>c7Y=isk4>KYuyf11^?esJUfND+aJL@Ks&Nfe zx0n>joXJ5hDC{rSK_4!Lp&j?h$4cJBn|TTpXQ1N1R(WJJM9WT@^Oty-b`o5%((N+p z_Z0P4+RDZ88p=MR(>W9MVa;Ce79SC$m5=!jR#dbS~dlzs4b&=`JmDLczq-F--u;yed7QEws0Yq zEI&p7M%=Q_Um%$;P0@=qXcAFXMNh10+=1X^A4^mBuA4yz+#FgoIhn4_CeBJs|r*5DLR4Z z5I!?js2V2|a%wuiTo@(k{wm~4$5$!Q!&is@7Cn4wZf3I(M2WHym?;K|!bEYP14KgH_)r6vCGvQ^W-vrcN0at_4BP673bOT~(+& z-)%m1=3rBCi%SkQsPXBp@XLSQpHH5K)uQw z+rcLd{a0u06&p~F{*8yj64sneQ?V}s;fpWv;b275Irv(gBcwcud5>;A#FzV)orEQ% zpp5za7~(rj1Lk5fT)?|T_|RtT7c^$Y=LW$RVn?~EIvs#V2|i5$d%P{?CpVI1W6ik$ z6I(FWC^OH(2;rZKGj8VRrvqec++_<8V%2 z%|#Ko#^HNRL57nkK8#+UFbRWu@y^gFuuu^4NaJ_-q-~k;D;Rw<@*^;|r6-q}TY*A5 z9S*4O0Ya;xNGGv+PG1_!9{>UZJjZPr7IgC?h%>)_#2y*TgFd>hcHEUf$#BjfWxOYK zBcuhqG46m=-)44SA${Op0N)|fpK*Z?G^#<}08Xp5Q}@Hr5~f?( znE7v~K_EwHV_#T#B2IlrI(0gsdcGK{*v_RGocVL^CR>o#S z5i^#kD`yYd5()5m*k65+Z-;BHf7RGjdGr$;=*M|++Tn*X2zDc%=bH0vkwb7k)EsgD zDmQi^_n_^YKpC4kdsGI=@GTl2@UJSsI9kB;mr(Ep6g)d}2!Sb6!xv;W63kOXkpYr? z6HzatrAW8=b11#lJs65iH7-{5E|>X40aOyl$EnAu-2u_@=EI1C)ZibXtuchDg&05p zLaP(8PRWqCgA^e+19`+$X>zxa(_|;~4iaA2rT3DEz#667oJ=5N^w~j3I!Fbo`82DV zL7hzKvV0g^_U6Ez1ykeC!H4Kt(vk<$&XZ~UsI*wgFeI2XxG_;XDcntAF5|D3;g2Vy zR{=3eN9K~dF*+dc1=`2ZIeIZFgwAmshy*QUxH+oisP6xQ&X}1J1Oafg5ErXp6BPZO z+yNjOcaFv;B{pH8)v#)>P(iM8vE4h=>OE{X%x%-q;DOJTm888O8@|PZJQ_zkQn=bd zY=c4>aA46K{!oRFr%?^1C0+VQLjYfYI-17|P*6a2YrKeViwISBBd@x<2YEx@9gyAL z7sBhrUKtc}6N4f36L>tQX+gQejDcf-=uHw9^jyt=!crgtI6#~UVbAnvfr}9#Mj`qO z_q^eABG6RduTf*T&EKduL$MiULXQZ=>fyaV%LDDEL5ovM*l=!2=%{u={FBJDfv-EGZUx6IM-;6WfkAPs6xtAB#Mgp+13z2otkV z;WT_Ss4`K%D&OUAgBN!KG7`5V<8aXXQ;@pUyaRiWbWI7Jvuaxngy_IIZ)+2w z%iJV8->pF`ZheW(b(xuNfIrLQ zBd(5Uk~|wL$4`Wk0G5d-GlWfz#L~50w#o5 zy7VH%fMgeAL4~8;Qp?Y-A;|J@xNrFsfD^mRn2h(NO3eeHFEE%$gZ+TixS_6;%t93! z$J!uWx&ly%WnNB|n2!K-mrbZkXp>a@zHI(j4OYkef!>AGY^FZ#Punnyym-{i_hsc9 zWO;>}Jk0zVMl>yazjIXSFQ32MvivmE4o-q>ha4Sd&bQ!P@(;u529svnVF=ztkDQz% z-RL7@-#vxIT6eg>)F8?U(|3ru2UVpPeVJt}GeUcySniQ4&5&mkMh0wuL30C2*o>gZ zz{*pmWA%xZs1Y2X`=yaY3s6+{If+uNS&hqSSMBFmXERRlTzDHsh~UkT-;EOSVnoHJ z*NI%QD=HQlZL|=jR|Gesek^XwM5?}aEe4m(JPoaY+S?1fXbFs3LuKaua+bnkm&QG) zaDw%H^u6W?0dJdmDVYESXJp#zLmN!A89xG5Isl8z(3Vn4riFb*@EtL#HbV%4nU4tj z0Sa?BQX-R>z^@tn*UEUFqwX)GZNtYbk9P|tLVv6gvh+Joy#|d#JzpCdgxd8JM zeW1Yk03+1>5<83uuRZe3=gc_YY>cxpt2#OgxFxJX5Cd)~Dsi1RwoAkj2q^vk zovP}jlfjwqfA4*sd!Ogt&hw_;Q*W)Os!p9cb?TJz-0duuwj-H8i{f6CHmI@JOS>a! zC+>uVr2LJj_5s<^9_73=RuWZwbu!FZBv6;j#&WpEPPvy*vd!!qCkO=M@0;5|+a~GF z&VMqz?3Be4Ld6ZFQk4CKgTv0E1mVq`)pJ@^#K7rx$`oDLhwy+23m+6tY&ZBP_!q~e z;73K8QAO#jqlbi`j@==bnfFn+-|7r!gTDtaT_ZJKMmVRdR25!e$=GyW9bRmwwDVmR zKVOx?zaXagazTWh@)rrAmr7FnGYCg{7Ezuswp?Ct;#EW`ej_0X@hMX+IF{n?c9M1R zCrR;XLv~5bwC&U$lZo@6aQ~gvlIaAJisF8bO7B#-KOL z6#hXlq;X%QB1++R5|YASrZO|QpGg}LTx`r5+;^^$x(ULg4G=TYD!wh0(n&05*k~w~ zyOuF}noqJ*CQEoaT(WZx@q;Os07tpg=o}_zbkkOH zE?gG*dTah!y}!SQIACFD#Jv{bW@Kyd#E{w-*yfk~=>K}7AEB33*(&%pxPZ@nbO3tOd^5Wz&E16UNmCy!>5&3jw&k5L zCutRmCG4Cy_oeX^be$M9Fw0dr%*$G%1D)=+wftRnpoUzXOo+< zL?ZgJK2~Ul?N>mA!QFDMimtY2u!KkhG8})+h~D`2d+FI~FC3 zvpZxB^tU96BL@#qJlhM(BWbho7gpeksA{e`>rfC?%aw&UxmHA#a!JrA<&Kf4_L?Lq zd-EEdvWC#ZrIM6m%?4`U6Q-!1(_?8n~Hg;i?t3mn>gnPcj*_yloWzo}3b{)^~}R*FWUXS@_*Yk`Au&*?t@p6;XK~~QHa^bzIa5|7?bifimfbAgB$3Hz z_%U9{;@L->d5>UjUm-%N{6YRKPKhS82ajdf9uE0F3LYD}e85K5C9bUvt%W4DQPvZ| z?jb(U)RwJ2yRbD;@d!P0A=^lUrTYpIVEEmX&LB%bFj9VtEp_QOo)S25o|Im_hZRp0d(FA*(3uD##oA0lI7 za))*FGq$a2r*I7RfM$-@95np#e$5kS3PsypkTZ$K zE~!Mc88|bN*0v0q)#eO^zi?LZ&rT1o=E{KdlKfAXP05?9%Ff*gm+SIioc#)C;i-yK z>?z#aw>+LTi$EqPm{V0bN>n+_h*0JD8>>>QvQ0J@{Sq>QXl|xgjXqJsQX?@&6rSU| z^Hff(&NynNcMMK2w(YH*>p?!|tE}d$SSz5b zCX1xiV&}-uL^->xdS_ADh1WpX3Yu15=iHDhL~bx&1K5o(?ND^n36VrN88*H{bR7pm z+eKCnagx1vJ0bkn61xgzoz)$k$3UjM$n_)mVYZ>z<{0hjvf`|rLd@;1;?|QGabfDS z24Ywwp)~&s%QNkH3-ul$GUG~Q>A6FRyeGzs$Qym9ElDM)(IWiLZw(wOX)Z0(au(>V z9Dpt3$eQ8gmthB_tJFSfHP1dE`vlFi%SoSl+Pg6l!Mduw)E-b`e_R~dJ|%BrCW7&- zcHXUo*=5)_mL=3H2zeMHYl5m%6qU0Z(ay%hf$7bOmlP*#m)$QGV5Itwz@w}I;rFcO zbmo;H_9lz%*U7s$>kw$6ZedfmL&DK=4e=1?Rsyj@RT{z&S^kN@hqA#k9Y5-8|1@L0 zWEqsp1+?)v*`ZJZ^i@WHPBxY>?XsTKeWB+(TTgco^Abq@Iop9kQ0OX&N6Rkve}olPi^6&G_)eFB!MfmpLfEF!95@wX~Rop!-6Y; zvAo>oJTP7(?Ep0>%{nsf;)F4j=4 z3}pH2S3*{z(?k}z5T=37r5{VHdZbTGW$kcQp1Z&vaYnE8*|^0-o%N7<(S^~ zU@znfi-Q98PNzjSIOw5cz?t)rq_o@Vzv_*UJ@r&8s&Y zLH@D&A;nVjvaxgo#%)%&l#KlV8qmh_W;SL}g{R8%J=6Imhk_=oNDibIO@yjB6XZR}T45%5n9&w5QdZ32SJjJI%c)<_`>8QZDtnnCV4?$qs*R;UOW-4n4 zmZykg!@ESTmQy3hJfPOpXT{cC_89GBonn5Z6j^3cY1U;K(kH+S>C{LS%j9hZ6|CoJ z;#o&6?{~h1;R&frd2j6&=-QR_somvldPZy4FFu3!q+X0ESX^hMsuj83lp86?TYbMjNygtOX_e4y z=n>2N^On-ys&#~1rO=U!?Zk{?xa}l}n$>!oVR6`h0ftGXgTtZz25m5H2hN$C2{|89 zzxsZcPI!kx1&OvU!Fb~8HkT~GOCNIng1>O=A?dp6{*xyKXFttolD^%rO}cn2wL-7! z22G&)(|S4A^h0lHMg;*yP2E39$bG6L5roaAB5kVREJM zAmISkkw@5$GE*w)zB9QbD--#X&=WP0`+pz9Q1zX}gXYY-Btu14u||~zYK_R48a`Yn zsI}eaCr8OdJzbdvD#9?|MmO)WsH};k4WZ1G9upFci&W{KgD>Pr!+LNctDFk8-1Bl~ zS@J26{$Gf81OLj!qYV4T3tY}B&Us6x&-8871G^wOkXFwv%2 zod+4v<0ASo+U<2bMG&H&ph;>GEaR@mIRarK(F%Hy)Jl5Lk2dR-rc|4iCT9M=?PaCe zvlkz`x60Y01NI=t&kVUv^!0jV<1B>`z@{8Tm?!hYQsHH8(RznPb{h&gcX~K{HxxB) zE7dc!7?tD#FPhw_GFv&o=r

0Hf?|l>wIlR{&RV3>mK)yt*td8RR~?z+-D&O>yD# zVJA1nQ3Yq$GI07%_hjCI*o$fW z2Gl1lc;d^zu$}I#9`7o=*c;bryEFgV!r#r=bwab%xIvdFcf2n34CHyJS=YioqsrQ=ahb1sWq}tcyHK z#<~&>HhfsmMjB&1Uoud_s2a7g@~T~&*w8KG(bFh9e=>p)EX`ji1JKjRzO|TPlM(f_ zWZx7g#_d}3chr8qwlD)8epbfU8QnGB@EAFu66blWEu50Sw{8fR<`pK_cq6SoOy7Pi zyJc(RIn(%>Jta}}<7%K9ImlV|LL)L2XK01i)PM=$bcDF=MEiv>1?;zwH?h!w#)6$7 z3k4v^0#dLV#8EjCJd+o%GZFT=PZatm!TZHpQ$scSMob{0mL`OLAZ0ibALk!jUy`1l zy0*lVDH*{+)IhhG5%dm_oO4zfc$NVXTczL-U2(rQ*!q!G=-+ep{e%^ zRA;@xW5bsZsxKLyo*J6$4WG`ffd+>A?GSqaO^w4PA($iZ`f{yId8%n3SnvN$6D(;v zO|a?=!ICyc;eq}ry3y%yf##;Dg0%=2)#9Yh`3cFwd%n(u&Xk%Xr;XM2o-e;3Q5{L! zNDoxTJe_*3Mc3;XPvZ}yU)n`En~_#Z=S89>qmc@&`X8eo>M)5_!^NqJx=GJ{tZzyj z4u#3~Axs+0>DU*XG+MzvCSvJv&OW$dP4Lip5?=bgw>4*=6*>!KsUnJdZl`jDF1Xp5 zj@kG_5>%8E-LMY1zsKAryEtygBCVYX;7M1s2$_!6toOBvaim_xdVHYyi2L*uNJyZb zUPq5Bf~P5EN2w@}_hYi{6q@3OJ91=CjD;(jsm>o@Mkp4R!ex$xJL| zapoP8Y?0m7Px@RsZJ@*orPL23hvt-hc#z+vl3yt0CM*hA&F*dD*_`rc948qv4=*Se zv|woeuW_41vgusP{~hk%;i(OH7}?-ph&anT_7NH+i6FXAdxUAB-%D?gS#uUZ)v9Ph zMb(5b*oU1?S;`%>j|mC8p1xEP%Km^-awS@^y*>2JxCwcl#%n2mVZ!Ik=Q$_}x@*G? z5l4|h1ilbt4oOa%uZbW#94^JOa76rKA>CH<&xH^*-3Y`OM<^k>=jGj;ai0I8 zyg70H^W`EJ&tpR|3Op=pOX!Zc(6G$6sjkUh_FQ(c?Og_OI8CTVPJ?9i?noa1`lgx`V!S zsU&nxL2>IliKPj6+b+1Hqs7C$pi=S@I0RQwXr)ylNU}--`sl&zC=cHnJ@$8j2UgOiY(NT zBDE3GxlGd@#&9IZF#b9^Y7^U zKF_bV)VhP+7Y4@X?hj`*bl?9n-OJn1{rfjD`8uMZ`w?;9ZRnOU<$qnX7=eV~+vv@X zJD03vA1~C9juZro^$;tc=Z9N!51RZ&^yW8clVm4X3QX_?k6#$hqF<=IF62dL#*4}= zofnmzhZkXK`~y6j4-cf&pdkbNcJVjf1*cQT5Y)3GK7RZ6L?^Q^-zDnL- z5J?LVpor7jYOh&eKE^rg>cR;*T%F7?fD-_?Y-->LCU~cEcT+sz6@hofs3``!Khvh^ z@wWDzWLE0hkUAcjhV$XEp-Vw7Kckch;B zpFUcQ!ZL=mDdqFd0us=G-q~iIy=g9uu6s$W-Yi<^3#7{^ac{J*_Q?2Tf-08Hj@Q&? z$9_NTfA5A&oyW<7xJP$W!ah;`9^~9~zT|X_E*rtt=o^d}ncEwbuaW^{2>C?aPz#}D zC^=7QF*#$OmKswqnbZ0)w*Ol8=q;23oR?ph9dm$lhJDY;j`w7~Nt;Z6u;3tk7*xk1 zaPGcf$2Du)EVlLXU-2~F1f>xzIPkZnvkag#f19WARfw8C+~MbZs%ob*x@mJivoi5~epz&mA`hX};+n>5%A?m61( z5)4ZI6zHCtT*juFs?wh&nM7TsAM0c;m1MF>h6E@KDmyyn=9_y{xs_C+AWX0yT0kv#-uYqrK8pihSijFt(hdC4xK(v5^A<1)xmf=KU6D*-gB+z zk(Qim-EkvjQzMhYDx)kaP2pj+%}sr-^%Xw}%emGayeJjZv_{DSxwuQ+3nuh9=3MJe ziYLj>>`A_lW~j*{mKK%iy?^^K)%cdmq?7FC%3CO{0%y}O$!o9P$o5}XVmD^2$47Jl z?0b~iVEbits+4^pnFin*?=cpHVU>9o?pU_X#J`?*wb>6zj-(Bj(q0PrgFa8c&No}Bu&n~3F0b*q%Dx?xrX*Gi?t7iQP)t)Q*{zfj*dOTn^OIy zUccboiI=+QHXdZZ5=r|$Nn-2w!7(~zBMlJ|A6p5NE@HPCX8dvwPCiRY_Y^kAxWv>t zx-ao4?Aw$8z(`ql663_ z32L~h@fJy2GKYX`T3{)jB>!H!MC1M>o8L;GT{h&ninZ@vI+94iRmf_>-W-`R#WHXD zGj8)V{+E1U(q2Hip2m9ws!X>+HHpu-yu2s82`gWc$4qzaSeGk*(yY_Tcjly098T+2 z_-sTE`4Qxc!Tghc-;n`v{)tBhU}v5xM$(=qD~28h(6@u-;es+`tF7GwpkT!|Do3GV zEh2zwF5H*7c0g0%zC1Rw?|28@-=&wLZzLJ;l z<{l9~qH-^7wO*mTRgOZm9>dJwIh@>@NShTN%4BhZrZI=HkRTk&tM9y*ymk~G%4@Lq zGQ*yUkkCJfL!XEa#PA^VcS zlL?+R%)Er^J8{#^EFo>6L-(^y$TD2cIj_kA4Tk4Oqm`sL*?AjM=sZV?kHqO9r3C4* zD%nfBM7M0xVv$(e$@wCI9pogCh84`n*Bunw_)yx~S5--L7d7Lomrm)mwj9QuCBuz2 zrdFKCo&Z?R4f0@z z!Izr&kNaI;oZtD(Ftwg z(>Plc^jJ37#0dd=e|U*Xyu6SOit>owRa_gH=fcND5rQlLVG%z&97!9UUpRwPk@meg>V7=Qhg7|jF2 z19VZO{DX9TW>Ft0vJz3=14WQbhCAK+DMeYT%;4PI=l=fn)FRRz9-=%rA>V*UWWQ92 ztE{OKs$;r3Tr-d)Nj&U4CP|b~3oAAJE6Fh_bZvt3E-kEc{sS@#vAv#_M&Vc&=B8Z+ zp%G-VOhDg88+fFXj>MZ*Py47#=KNL_^t9NZ0WsK6jQk2kkjS|AuT$&F9+|CT!juW% z0~mO621Bpbaa9(J51LDj>>F)snA}O7vLViYt=*cDfIf=*NGk^2J)7SeT$>n)4_^uS zNUi6zK0GMVl5F3weGb7#Y0zQ8B8t7Tq?wY$VQZuuQ__P;^kBv;<5H6-GhlniJ%db? z+bXySPn$IO){B~Y_>f#$mF`P%xMN0XWaf#bn?^MpD1!72%RWm;>FAi^r^Ob3h$?nQomtvpq#cObXj#&Jx7VFYK$(fU4bQ&~E;z zc7E2klY*70;JuEKmb)f$Cj)1I%*nmI}2A54EQZYjxsj zR(Dsha@d!8A;rqLXJ(164`d!g36aa!SVwdwLs}h78Ocr1qg5J%NduYgZq@ea&AiMC zi;r^PI6>%|MBBRO;keFyRPn3%7TbNvX)JdSirr#~KTz$lgx&c&JtaG>$~L8F+3Zdx zkaHyiS>zM$0Rp|jHla0PyoYfdC>VX5>-$>KzMU>`)X3yHL|lRZHWK)%Df^j2vD2@cV?fbh}xaq3;@ zN8f$MvfPij4??M+iUyviF^^<8v*u|W&r3nxg@K`T3QuDuP<8VKonBm|t|D*DaX#EB zXk$`tSdlE!vcMQtAaxW()D1+rYPXYefmEc8HXs$NjfrW(2FW6Dp~wyfcpe*A-JGBl zC=2oy^;gibTRZ1@KWX(Yq zP@9>UWbL5l4Vm_xChV^wmpHovR(>VmwbasE7#2?y=%K2->vfQgIZDRf0P z1SApLN_NJR4!Bt}Lk7nkOk(J5?sQ#9cAb-?8#HGJ5L4@hujs#06MGxGl&y`Ze84?Q z_BfD;O+bG}pLJa04m1A-lAL--x^;@PeeOphg3-Ql9@lXaML!}YjxbO%q|=Bi4392ieH{lA!ji8kl`! zL8mV$`_(;xL%uxUMs~cxL)w+ZvnG^viB*_Xk@}Gw1Yw2a5?zxrc=D)Mi?>+Lc*1ft z%h2gu$-h&|MkyN$Y+9CK*y3Mlg;G9{OkjH!bMaS)DcC$Qu5&Z@x9Ng&RyjDq6&UJl z!TvhFl7e%s5|7%tE@hO>=l>}UG z3>%4)Q_5Xc>w5E&$%yeYRa(Jk+1z6Kni%2P8NR}5o*;={BEm(|2@Az}h!G$BHb*Jn z0P$Cb$qt&teLVSCmB8=_A9e(9Qb{cl;p*$U3R1p=c3^4 zQT8=uvo!gPro=Ovxb5+}I7OyDK>wNam;YL3Sg#lcl5-aF%B=UjoOsG?FWSQ?g(vsO z_I2kC(o^<@G1|^~nm5UOI=h|B{E@WFRT=KQ3{7ihOV`YdD$&h4QRF%&mXF{& zNL>zH5@ttzpBVj)o_-cqnU5~hU-zo7=c=#YdE3hWtTs=M(Ah8h&n>Zcmt^hzssdU0 zCq=m8DT2R0)>$PO&;Jr3Vb;f1{wK?H(KNDJ3l)1r(Y~x5!S~|)k4)zJ`!CIRQR9VI z%HmYPb|x6r)~m8g@;|Ce)kKdO}__G_gK&5LAw@^8O?n9~U-95tTt6gF`RWk?`QWLpVdfZ;C`r+U;Yw&~TbJ~}S6p$YrFU~pvS4ycpHyp$e4j1k$^-$~~%j@jM@4gFv zY9;K5VBo&))wpnlIPu{*;v|Hx6DKh|LmYRwP@DnbN#YC)Um;FXc)U2Lg)_w&6doT^rv$PWSifQe-?l?(4uJciqc3pS8 zfbpHbF<^p#3F@*=37jZkV&~oFt16m1{B8L(Kt2r!FO~O!@;)$JDep=0o)o@8-cOVF z(-dC12g&=O@HOH;Sl$QA^+t8ws@BQj^W`1G$%2Rx@;*e~hlD-yF8^NjgcCeB>?&e~ zw?Co&krj!7VRwxkH_P}Rg~&@eE3#z5>$ncCx*)ea)*n> z84#W<&cJZKI7#8l#5paTEzY3udEyKXj}j+2oGwmEc&Io-!h)>hs>1uLJ>h*+^I4f~ z7{yog&OaP5srD@4sW4AXOH6kOoJ))Xxi6Iw^`hn>z)#N_My+WWD=}Wo!2>{T3ePZ} z5j;0nZkmeLNjJpYWXI8H5n=Or9}37xP@jGnMB$ zo?Cgo!Ly9#E}lUQv7tQaJfnEdG9v|Lc=Js#VJJ4t`EKrM3BFiz@iY705difiF}vrgpO_V zUvD*Z%VyE?ORSL30|nt6)W$AoJ3q)@N0*bBOUbe*iR@~DvZXr{_UvH??F{EyRvTK$ zgNqPcZ@fV`HjP9_L&8l%h9Xx*&>&SaFXIjHy9d-`_m-~LtKuKib~<@}tKR>|$AP*QMyiG*5L zV3%pD3cpPG;B`KHMOQ2Ujfm;)zj+jj#ftHW41;TXhd~H`)$5a_*FW)H)$1X9^kSP{ z9I8r<>R;ZnRgehj&=?H=ASV6aIYp!sUJtj_mq&swK^ldplmk%^$TH`Nzmj8PMvGt^ zk8)BifcwX5BVLH`@yKIMfX7340OiOY)CLJJCE$I%_D814Dj3SSK#814+#D;BtE6hQ zHbEWQ1nhH7p_Ge7 zoIo6;%Hb<&jMJ`>e^G8$VQy zd`M{BjVPDX;PCA!Pkbn=gWaqi;Q=;vGzR&3w*N?9#p^rW-Y;^~Z)?$Bz4qoG^k z@(6zx!ZD>(BQvd@`A ztmZ529XqEbG4&7HF)eve{p~5o3Ysoar)x01Ui%qRF$+7J?W7ghs4brUfj*f{W;)mf6A2UMZe+#C+|rXY$B}3+F|k&3=MWfd)P`gO9Y3^JX>=kp_v1n3z;w1 z*@tZR!y+&g`oI-|SJ2YG`~ccNVd5cwe0Yqr?j-Be=EOsEmVi67A~`(FcGnXj*iz8! zUM?{sTf?oJln%*7q=;N;tyRh0SMD20hTYcS+eV8fna+{OB%&+_y}>lTM~A!gUxj;0 zg`;puYOSQ}E!&}9tMb(~yH?cZk37s+s{00OdUm4Thv$;bCCnSQo>S`zHsS&{Uyes+n|#8O|gaFQuk*GYye@kWO%}^RQ$kRuhvhrK=@4 zt-u-|!QBxyDaMgSABiMF4`j+8=F#54<0ldJ-rEx+ya*JO=ci>uo$AV1;2{;Qi*_V_ix&11`%Htjj}3V6QjG-sJS#k=PI&0*-co*1>GghMSm|% zaNyOBme}uSe*Z*CwRF?KkcpdEk9A1XfFC>?GWEHuqQ6I&$)kN zRz{)LtM{zA6JN&edy$hURPQgf@7XE&7u&~Ygc3h3%$t>WwSTajXv5@n#J%cc#4Dcf z(d}F_?8d)K$P&Bg@sg|^laYaRT|L$F^pg`>4sb#wva`tkq%iA)(#khV^AC9LU7+|v z?$eFjnZcI)z>=)CnW15yvM?CPd0AvTlh+mwy_y4&`LC?{fO-|J`RL9x`HCFKN5;T=*AoeH9!Pp_6VLuBx_#(yhg^G}{Eq)x9&q_Gixu42<9vdLq)trK-f_YLw zjM9r0GUOBY$`LoTmB{QB$)q`Oh_uP_6{djD>_f5^kPPhu&bGnPd=?Rh#0%{cY>9{5 zx1T&2nQq^wDl&O($>B*yS`+=FLCN8(>)$%jROHczgVzq2+{8yx^)wZwhPf;HU6wbG zVN$7j!Z2&~>9mq+{ugOe4*A7nJ4E=ZmsS4G08R#~N`As;yYzQp=q#C`3o{CZin9f@ z9Bao=BNjQ_wvy?l;&eP2AkNwBAL>I`+lb5pEP@kvxk-aoIK22c2jJ^PQH!;pVSKSo%cZDk#0%XBb9 zdTrv>kuB5hd*0DK<#`Y_xv7wzQn>{yRI54|D87UPBv+U>`(D|q!6utoj*)QWuqU;M z5mx%(Vb4&xI*r#+BCShZC1dfY83lrqhHXLuqMWKSyJCy3(Vdg&Oh-%YEv5ELzTkV; z6r;{?ZRi{sL$1<QF$H`nse}pb#$uWeTBc}h%Aekrt+ThUDNaD- z-(}f}tuvSkN^F0-T(8?auz4DHy>72tDhuxdEC2JljI{&Myq!2Ga9U(q(vE}__yB!E zI@FkP4SBF{RQrlvc`2HmbwK(Ds|`+DXv27sG*7l-5>J;M=D2Bz`CDsQNPb@XlWG|be@})KT3V|54ybPErx{W`m~y7IZ?dy4rs>pNdkP%9I3|jN{m=e89Bg>& zzk>r$@GhFX2M?Ec9!F%&xuW2)L4nlZw4``-4`y{U%7$Cx(Z&$R0x4G;q&ypg6w6@6 z+IK=>CE*}EH7JUt{e$YmpZ7;ilb~kZg{MHxjFtZiHN8B(^bd6b$;KKAcLM1UsNA?l|E9$DnR#P2L+Nhr|_+sYf4e2=K4f&tk69f#|99C+uQf*#i`Ib_*GfW3u!B}vRZ|h z%_g#hcENC3EInjDrOeJX-nM7g-i`r9| zwS#d4G6|ZV0Q*kgdk*19*3RZB+}w7u^D;HkMqeOojxYf9L_v-8#MhT=bPE#ciS)v6 z7D!K2SB8iNDt$3kc-C~`eqxWuda;#O!k%p9t6~fx-MNiJib1c-xdT_TQA*DhzNjvL zWEv~3x^Jqy6-&#x+4w_H(2i?LLK!W^cH!r)$wvx1;EblS+zbALq@Kt2OOH2v=a`;! zoudz`tiqlmwSh?tA8mpC%iT^5QQ_T3I4|fhP8ip4xh;Ponr3m!WtmeEU5hDgTd@hr z+!4!5sXI(6v`x5@%D1?C$kX_vco2t8GOzP3lS8x0;Y>>4+^Tcv^b-iN6a3XVoK~ck zrS^x#tzSYfC$>ztlRGD`^>7 zbb_aGi+m2b88&Lm5}X zQM8wE7%=~fx*P0UUi3jXJdd|kqu=Pfz^c>pcxhKPs>zo^E}Cz7QH_-mij+8y@>vrV zzMn3W#fwVGKOVzlb%Voyi=^Foo=gwz_M1w?Jr=+0X`v%vH8UMcmZa(rPpI$(j7eUlKokl`o#`u*_)e!uJMx4av_|5P}E zaYD{dKeI-|c*usXvcb)&;cPTLSVx?O{ljs()VCavnYo==Sxn0Dv^riw%(F{7SV3PH z>~;r6I5+*dr$6SXSK(_qcfuz)?eNl_Pw__Myo=;owXQ7s3jFT+YtF_L^SLSp3YISC zGXgW*IzxoFi{8!BSMf0Wi^zzMT@gF=$H--JphaGnx~t6jgzmEL3A&Ywo}ZCF@=XX8PomGEOIR!;drpi|U0Td$dbw2+&HVUW2 zO%lFK_WmLB`gHZ2ajtrOIObGa8o_M^=w_<4kiVF;{w%Xd@L1yVvu)T<3>vsx4Rl_H z*R*}I4UkaXj17lvIdUXX(Jg2+JX0U~Y}r?FltEp~g&D)cC2G^g(}=8t+OppbDfuPu zg?ZxxNxqE~8l%UB=spSr{c?ppYp2B@IZ!&HrTFN9;$REhJ6Dd#bcW&XWGNsUJkt6B zk-*)~BCxE1fy3U@1S|Mu+_Gs6t!PhSNXT^n;@ryoQP%1VvLeq_`v$kbP;89Q^QyiVJc3iO6=gXg|bl_G~ zF4jI_?SqspB}4nXs(lvVbE)>(seNkj$<#i7(>^QkIamAqO8MCJG&Y_?wdeP=XOs3! z)SfG~rlHDv`+;-bF|NqpGI>tYlP_8P-OU_L_|QK|FDIiMEyotV?Tv;w>;C1ii95$GT3=k zQb2ML;>IE5lOM*&x~PTH1MxJ9He=5sBvpy%o)W1b>Dw={F4j=w@l$wh@>~Ee6R@$63zhMSv zyFkJSiE3kKk;UrwUSz&PI!Feb#?(T>jP&Tt^e(|prM;iVb^BN4V zp5(4_1}gRF-H3v^k4Y2mc8b}hp;~)YJ}cEFZOkw;M2(wsiU)rzn5tx_nxI3F>d0zS z+EjJx==>o3yOKCFQYgr|PCL1qg8b%JJ*=_n1ar5lnKqf3cA)d*ASQ#ioTr{wBG=tc zCf!jaiMyT0@PgZJGjove&iYEo0_OP(o&*W9SO+Hv4N5{I?|nryBbjn1?S#=oFv|L& z)Kf%p_WMHL;C8s8VIJ`h5S6CpA;3P*53s|qrSk&9`qs8)bWB9x?|Udde6B!U6FUS-2&W2kY+{#Z zjm(F>hZ0GkWs?l_tTy|l7vA=*J&^SheywBM!xt2-os#IY&+x6iX#keDPEL&X+$~&I z!BuZ~?s`z7Uv{5aWh=>Pg!2Nn3eKpHKx}N6Xe72P&`je21Ov;H2FzNtM@hLPx4SnFn|}P_cR8 zvTDn7C)zV7M$Y8SKfX@2XIAzZ>V15+*a7e~uF-MYKl8l*S+(bekE%W6w|l-jUiltO zyOJ=6JvWbgM#s5)+A@iQn3L3Ijr%c>TsJV*J&NxoV^hT5K=Hn}iW6GNc}W{-jk{c8 z+;xcZ!GvFgx=&hAr4>&3w!{gg{DbBoi)Rn}Jadv!2FpYrD3j}qQ=T9uZ2ZWrgu_k2 zOb70q*La@gK{G0|EViwuO-6eyl^hPt0vRFY%D;QczB#ly5~DI7T0s2x(0!!p-h=$}MlF zh!o>ivQHg%k19%P_OEgA@1jVblHe}h_wg>z+dLoOH?B+A;RutO_F7m8rD&mjD(Rck zb}2XY1CmV3mgMP9)T!7f7zQStC2K{xreBC2oFYML#vhv+4`OFxTJ^eMMRQVnpIa}Pf>OPCx{??1TB(JvZuO1vH_8LfS+zk@v zu7^$U-rgBb`;CPEG@SA?TsrZYRDSzsH$S_rwT|A6$9odyN#C`1{{BeRzAg z{H1Mg@7m;US7FPyZ13)A=XGS9c0l>x?CuaZT;9I@#I4WbKdwXNn3{b|=ltpR&&qd^ z=e})kcm96dBPzqr-)BE7U&C|uZ9h2YZQT9p{e?LnY(H53-u8nxzYmf}?ocV*ycNF= z!lj%iIUSrc?qYG@nd4UQJ99=3Q6(wgw*5pos43qXQ&Pd|HL8F&r(G%TzPkOFZSOpo z_Fpvs-I-gq?>nCTw0t;DXDon1tNszVn3|-VNf`R$Op4*$I6%VpU_cMw>S+Iz&`CP< zI0=1G-_Yymmai_QTb|ELi@Zqx{5vm-)RD($6u~*dS|yw)`s*<{Mc1dc`-Z1Qjszog zGH|i*wmF5NDG9il2&kSQJ_pbWWnX18J&J9PiZkiw&F;~}66VSCgRRI6^)glKhS(`? z^{xBMScmpldg?g z+(b&s#OF(qv>)*q^GU1_Pif3pvHhpmYyQWfQ|~nwAc_B{z2?)L=O`C8Uxzp~@Yb;# z$}f_RNMCz7vpstZYZ={BPIE>ODd=@Mxjm7?e!aK6kA$Op%iCF&{mBVbvK#HfF6yBe^TgbP;Tz%fuB0^Tnvq~6E^t~w&GOGMh)IPVM8-u~kBlZ}oud}3!FNX4-A7uJ{KJr1uJkD6I5SBw_|DPr4eD@I zL1Q=CX`apRpH?@>xlDaJ5uR9ZWM>@Ks_-6E=g|_hboK3I*vq$gj?D3GaNQXIsaQWq zr83SD3Ke|kOXW38z9-kEZ1T#N*VUI#L?oVv5x|40hq7r*;nm?Na5VdRb$maxU>CXt zT}1L+Py=4vMGsMZrC;!gNSHoVDBD!eats@U9LI1HN!u=+D~o^v8DD~E*#<$ZRR9)Q zpE++qJMj%H2YUs<)!&H^47H1%33j~0_rQa}wgmJJ3hkn~vJYcPLXa(DvtPUvt963> z3i0(isZ6-ekDiW1Q0cx5D?NzQ$d%(XDo^y2aBo>^&%RwPNmTBc{D-k}K8KkIpF@XB z6sZrQtO8XNrwCBTW|Q?3;gJ7$E0jL-(Tom|z$UBULb&9clm@IhGoOlTmXK8UZ=n!H z*vXMa@B__>$YxVrY9nQA8@NbhYdJZ;EpSopE>9fiH{SMItO?Y9GX?iT+^dP7F<|0= zBJRy5xf=j+X5k?13~ozRGEO-xc{vllZxd>>5N<&Q#fD~bo3PO2;D-xnF2|Gksn6aA zujZ5UDHr-gb!cH>bHM@64X}7a%i1wBj$6>Sv!G=(@pl#|0|nu1Rum$-ko#5?((-(2 zu=5TjljX)32~D<%l56i^@n@_kp&~1sC%~>JbB8BW>%T1!VM3o@3_lk9Fv;_mb29A2 z49cIhRp_JVFWCRz>iG*R=}io>$mkxwYR|>)=|RZiFnP0hTQ=yh<;ia2--WG8HX8iU z%pFn^-FtYe(P*M`3YskB5Nhs%G}NS9%`3bX+v>)wgBOK&AGewBe}+A~mgcV|BHB-F zE=$r*vhPSssvMmxN`2;Dz6Y(uIK1MG*MDlSc)a3_*MDfQM7$D>*Y~tn5?)EhYn}E= zrQ7i&u!c9QYIMvFK> zs{&(6MX969D#WpeG+Sk1OEBTvh-ON}_IZ6{TjYLWpNfnqU&7HbVxrGZwI^4YV3Sok zmVKtxTvq0MNY4_PM`C+ z*b?1{n8GwyThC;j;ma`2Bk(pup6I-j0^Mvva%zF#~c(K*4lo^f8sQ|7I3l0agY z&qjq+c9HN^)#02-R4(oOobMEF@K#tuDOu;w@X&hDTp}o1ajH6zaX&-QrYKZNrEelk zW=TS8iM=0DXTh){&*SB2O0=Y7cDxAI<6OHexg>vo?I7DqN_e&;e_!no6le&LS(LxK z_H87kv7}D%m_1^+(4;R8NdJA_xr{m?SsWuaOKk*~&pUU~31#QOV;5w!I-`!Z2Rq{a znYY;}767ZcBOXOk-$W~+wIQ#p2IqhQV1U}&L8@!$VP>iAqsgDeUWWro z>tmvlb}X{fh2jO@ImYecz9X(gWWN40e0D)nX=PdB(E}w3`>e{glCk@WE4NO!hcOza zq*8U9q$7oK2_FAocFy7wI6zU7&{mR5HoI=>4BXJ?$868oHSsg$rZ|AoVKXI1+e-gb4BhJNDWB#m!Jg7OZ0QeqRE7Ehw-Qk9%K z7T@|kCcmtG#ld4qwPTL74)hPJUbmL(zlU;CR9*j#U*Ka_D#cvk6&Qt}+walellAw& zr!4=sj%W!Uh-_U)pm_h4+&;$r-$Mg8F`n2}C>)xE>2h1nOB4=G4NT&d9ihUZ=`6g0 zt={6uuE2A}!46I}0M{YO_4MpqM)MepT$P zU!PG#7$7eM+doawjdlyaakq@-fx?rO+`UC{>+#Q%E9 zB2zv#m*(;0)UPgg`4>Tb$pap`ly60u4SAJyi#xDD%X@G|dSYOP@?GKey8m;y*o+{^ z3h!tEWOf!{3_u{m`2l-1lqM5WZYX;>c2&f)xjfUk0TQ%sXOWj~C&rklX67xjKigAp zxgSmX8mLNG8?8MFsoEzwR|ZajEM;0%$9bMpRK2MwiL?IjSf3BMyR`56KECyy zZHj%DFaO*scB#e9CpvCA`w+cZS@xRyrB0^o)A}R~hRUN~vip2d%MhL3us$D%(u0WO z?Xfx8rDZz8JAJ&>LPdXgwkb9|jgh1Mt5I%_EpeRg8vf*lypePfzZjL$7Rpt42wF_L z#P#V(CV({K|NH%)i-Gj*va)cwE;8;^?q7`iv~l+u_kH7@H13%@bv{=cce-)QjazNp8spw++;zr%z_^bZ zcZ+ck824l29yjiQHl5$;#y!utR~Xka?sdk!)wtg!?<@Ecdc>%%ea3sZi{jE8uxYMMtiE?cGXMHQMc@ZI0e`A zbEDz=yQ{i&{I0jO+wRrz`-e+^Uw?mSg#MoTj)wb(>koIms>82)t#7(8ZGC?4-rG05 zt_eCkld%3>rD4Q3>X&5T==XF3!;h+8rhx|;IIkaU8JNX}`jz*KKi|Nz@|9mrzwh<^ z;B^L$mhXN8NAurcU^boAZ)3msTMTSIyS5ovLM}A14qk~-0yp8KX_Qb@ag^FbNazv11FgJW%T== z*$=*`ADm<0Xn8N|_dT!Q_lf@t*8x73fh5EJk``%$-1lsC% z%)p|eP7Y<`~3zUYvQ*U_-q4r7}#szq)9qIB(&<6Y2fn= zTxQ^C{2BuzZ&AN`14r@SXkZaI%5Po2@aqj6E#Ec+N9j+yfus2EFz^taoU6;g(ek8T zt;-+9PmY1dnegQX&Mz{s- z>5ozR)}KE0AK(4yL1w@B{rmqqlRrCe>Nj`pZS(4?=10-znmf00epTfn+?siHm2w3} zG+dp3K5*1y?p*(Zg%ZGc%$-|3ug)I}FRQBbFRhLGknW-=Fn4a%yxSJ`q`Q1!wSVp< zz4<8k(jK_RUwg%6l&7|;&R@H*5~Tt8&7B)qGH=ns+m}?$S8(*JRK&ZaYPs^1_xV+e zs{B>1NxWZuwaX>;Hb(P436Ie{H{GH`F0HAmokw{E$|jujpDa3Mb~cZ^^T?RYzWPGf zcwFhbU|#Kf z^{qmXw&eESaLc5Juk4x@2yPZlzbX|;XJM^B04C-ytgBfxud-@!)e^sV z@w}R7J*yUY|4mUe-h0FK;>z1wq>A3}|FTFGl}l?@eihQB-hugkZ*7%7P`gAmQ$>;G zs<>{}tYTFPzUy}F=@ot9QztC_KiaQ#gH%O9SH;rBOBQ;i57jM{UQyADBt24M8&%5J z|NBk1^a)Y1c;1qEw^z;ocS!9Y?vzNC{Ce^FPnhe)exGnr{P)9iY-qyIU%6!7Vur@T zB@6uvp&WNq)q1O!)_SA;BKm!vziP!oe@yp}4MjRVo#$U>*Awo)RXC~lKjHD;q~B+} z`xn$!&6^)H>|IfM<{dM}OY*&N<_tna~>%R>nkDA-q$TF0K&@?VWks zH<&TJCD5^$d@JTHS-NE9;-!H)FYQ-VSHqB&h%IIwbNTdqTwvUJt{T@uDE4Ak9sif} zz67_@wbWJXs^YC0pWAq=Ro@r6F2PSfs`{>f7r)N6k`yal3y7ypKj?DjWsxkz_;OJUV;2-vbL%>P$!Te;aG!~XU z*H~EgO=Dr%ZH6;okkvsPd{~C!1Hqh{}NZq^=lr<=XX33?r|O|*Ipj^jz+!uJ_iH}4=H*;f}a&d znoczt_kQDUFzz>?5ZSKjE~%&d?obqFitAbpLRXgZeTJ%k@Ap|QsekORVyez3N*~u7 zxWl+P)AaXs#_cd}l#Y5W{k_7t>y6uC+?-+)-?$yd&6%$KqjcCZaD{O<7`HDS{Ac<+ zA3XS>o_^@`V%#W|jne3U7hmZ6qL_F>oiB|+(7%hn2s~F(hDD@vhbqfLd}I#krS_Nn zFG+7H^H3dWOKGLXu{q@Qoy>o|^IH@L|1LdAqn5fa zaoyf0ezZjYKK@E_miX0_D_R0+1&y_T7hm^?T1pX(D=i<*z5hJ=&;4hG!h-HYl7h~; zH-%H@FZo4hi)_m7cLhjCxP&MVF`wEjKSh~Nou0(4Bn`o&AC4iL)C4cm&WpiJfIKRJ zbl>XRQoVJRPEh4qLAd_>Oy3gDCmrdrOKG#)_`l*5<&1Wy|4w=utGdQgX2E9PQPA5a z($kg^Lb$G2en(0eopYmki+gH(iEj;fzzStFqUn(NjcXaV#JIDJTVdP<#$91tZa=S=x~?uUzD#`PLE&$yOxD~#(m?keLp8F#&L z+l<>`-0K5NT%}8wxMr~~xdB<+EDkG8tzGE4exBdOxB2r{qAs*lyV4$ns}oPcF41q| zi~oW^t@^+_@l`zWC!Ij8fu_$};+k4@o2ztQt;<(ai@VY_o&2W<7P)+Z+g;aJ)wpIN zAiQ?zGFM?$r9^iT2U*xkiC3#$@FRZZ0$kEqDEuO6s6`d&P+I&0w+HIX0+}@ZRf}({ z68=$sq`y=k@`&}8{CZ$tDfXLrlOVYJ~VL&UbBFukT>vO2*@mE`T*`!HOGJ_MWw|Rx+z?gPFKh8g>^c0Jt1^@ zvR>`=BUzKatde`y`FeAxHHu}>4Fbl$~=R*HU84FI{ko3RnpP<$gCmx=(^)=@|E?v{^|UMN7O%^ zziNKvq07-5UzbN_8{Hvvx%51)dA-~aa$40!+P zFVhwM`{!@}>|?Pk#EdpZ{Y0FCX~TuYdE~-);E) zAO85KKmX;yhaUdxBai;=vBx)V+Wf?mPd)uiOY4@c+qUo6*|zK1-Fu$f`+WPp{V%-u z(#x+Lc=fe|ufOr;p^mrSe&^ly-v7JP`9b)@k3RmS>(j%Z{p0f^M~`(MKk)^o2VHUT z35o6j1Cvf0G&nhBh$nUE>1PZ}JM*mc;b)IHXXLq~yrajA%@}vy`I#47m~~P1#p81> zxit5(%dfaHZ^Bji$ZW4En0Ff+cGb5pSon=^E?T@~Y0bB4>->Rb%U7(tR(Q(`S^FUOThwYu8;r3%d*Db8gi5|LX7m zN8mrsP=K6j%_oWsOMZCHCZU|^9A%CE-2 zB72kH3ImI5PJZ@K+3@oxV`K>ds$l&C6zk!En(6!#cQ9X$b29`CQ{2nr}$nNB~ z(ZHu`(A8q#GYs5j;9&-CH*jhVc?4lTyNlP12-91_TJ^U&cIh_&~?9o zuQc#_1LqldgMlX)_#p%9T`$_-z}J}XEe0+yaGQaB25vX-6aybHaG`-a3_R7qj)A8c zxXZxB20mus=>~Q!(Ct6Nz)1#<%7ar4JkxwnH*lGOy$1fefin&K|FQQb@KF@q-f&M6 zLN>^RHS7Z-1ldN25SDaL2+IILh7ceK4@@9|1VS=qfq*Ds)5M5IA5>Hx9YTQYiF-iQ z0TH622G`&^B5F|7VGsNFKV7FM1LD2+{oe21_jkYdHk|zHU#IpuRozonGp(%`8=2q3 z`UvLQldy^T8rGYc>v#gNF!y3^W$w+~#@vUww$9yxc`oaHnHMr|$=uG|k9iGqf9Bff z&p_r5)(0`KXWmK+&%8Br7xQ4|QVM@gX0B%*!dzRwZ^K+$zi-Rjz~S35H!^R}+{C;C za|`oO=2qsNnA@0lW}eHu3-dzeU76dNcVk||ygPFT^B&CWnH!ipnfGMwV&03nv_!Pe zZOrw|dowpMzn!^}c^~E`=6#u4nD=9DWqt>98}t6mbD0ldUdVhP^AhHRnb$HO!n}@o zIP(VPcQJ2dPH)DgtBLt==6*{>{pkgfbcHe>!90xlNahjDM=>`umzl>iSC}U=H!;s) z9>qM5c{KAa%*QgXVLpzzgZX&o^~@(QcQT*I+{Jtnb19YQALe@IG0Y9j?`CdfK9#wN zIlUc_E(`Nm=2qr2ncJApVxG(V9_EG2lbG9?FJ)fCJe9eF`C8`n%ys+#;$-f_T-%px z$=tf4feD06@2Va(}Uv~)!<4`Oa+-kEtk^HI!g z%$G9HWv=4~v_j@S%)9x1DIQw2QjxY@66oBd=&FM<~n|0+``Z7QeR$zeS7B zyhMx7yjF|PyiSWBCgL|}@tHSj@tHSi;RlHDe)n?ynTIkDU>?R?XB6QhwD8Q$T6pI1 zTKI4gK3NOTJVy)9JWmTBA;NFb^vp{%{b-@D)%48kG`&ga8#F!hMvadZ`X-H=1^3Gk z^$&;!YU`N+DcfPO;AxgzhLH-#)Z5FNvp(k(LNtt zwA&|;R*Q*?wMyE@r)!bAnu^a49A2z?OVEmNw7*Ihou8nK&PmXf#_6Upw=u`}HB~+B zn$txq#&prXIbE4ZLEycNwCarSJ@E=M?T*t$E8cXapeA%>b39t@r7P22FOgrWx_V9L zQv_ecxShaEaaG3GQ<- zWH$q`8=!rzBKsj8D}t+bG!R-gd+==N%~%l^7ugl*r@J}rzE8w{h`7kE24Xiti=T!X z5!XJ4k(~|1O1`Eid!zbl`IFrZ1k=Kk{SnvlCp)C_XyM5osr|J2XG-^iN4e9_L~D~? zYY(#1fha8xbL39x#ktcXyQT7J>5=_X{dk%{X(+!L9{ok_Jw`oWMJ+&fP5Ei~uq?Je8Xul^ zL*ry1VrliFa06jQQQU7iYP&S^RcicZPUFinJdLw~uu`pF;xx;6cfZlNqk3rNUZ&P_ zio2e40>slU#Cg;y?slYcO6R#WJE!qVTpJfOZi#F8)A*%+xw#!^JbT*D0=3@F<)U%! zSxy@7o}9-0jhxEe9G~U^Prgj;|7JbS51#tWo6@8Cg8D^kr-f=gC%~e_Me~SfKZ^OJ zIYlwAkj2ebBZ&EhQWO?;FX}@rGlqL6O&!0@{klx;r@P(lEasz$(lpLr%qP+A@gnxe zn#)J`Bg!qF!;AT3jJsdx{zLPXHr~Yi(5z2W>+d-~P1&;>>f{|oub_37VQ+}p0`9h zHP7Rsoub|2FH^M>QBTiui1IhrQ!AH+TUeAU#yxICxu(0zDasY&Q4X@V=6prDCb-M3 zl}qa{G2f2yDCaUfOEiyMk^fk?{fPXhxaR>a|K|CS@^7{`k$<#%z7zR3mzT}}lJ!R6 zujl-hi90WqL+e>ljw$YPiuRxAwnLGR#jO|lOyS<8d7JF)*7?v3pjoKMC(dm@BA@2* zB=Q;SuD@rxH}>l?btbs6{8KTK#3j<5E z)G+^wxr6zO%x(#i zGV{lo=P=ji<2>dsvwjQna^@!92hg4qOITmPdK>58l6fubwSAf#*88!(j`iD_H!%N@ zc_Z@*=1t7sWA3+JwD%LtLz(Yk9>)A#<`K+aVQyyr1@n03^~{r*YtP9!%pI)HWBw`g zT&|x#^DV4D!n}m}x6Esqf62U#`TNWpn7_)rk@-I6P0T-I?zcg-M=|qI=9SFDn19ba zg84t0o0&VA$1{I}c{1~lnCCG6fq5SDubFRQ{vGoY=0};=GC#=N!sGEa=5?%3Vy^9L zYWt!MtWRZqGRN=3ypi?ET72%G0On1sk6~WI@dq*Y+bG&IlX)S>4`d$7`pwM4nD1a7 z!MuvOnYngfk7xcG>yw%9)zahfrtQ1tus)CV+P>&;=6S5w_93@$d4pKLh4tF!iyE%) zVAhwgK8tx1#}8#*%lc~Ob|HR`X`x(F`ve~mdmSW9>IF;^R~7xK9Kch){DC}zI?~!y_5Cv ztj}St?L&`bp3M4n%pHqF`9qi+xxW?WIUGKpc^>oW%u+aX!urL` z<5}OEc`fV3>KT6jf%&7XFXZt3nK!V08S_Tw>zNz4K9S6uSpOh%J?lp?_j^dR#|GxL zoS%_-DC_^lJdC+mwZpy&^K90ena^OZol9xMJf8JWF;8Y5$2^Dm)6DA^iSo5&p2zx4 z%o`UA{V?WRSpNX?29Dp3c?s)hGIz4RJ@Z=D&thK3d<*ji=8rIMWc~v4CgvrYp40EZ z+%Hd*H=enR^&OdqvR}$JYc%V_Sig~Z1oK_Y&CHiGk7vG>xwKg1--&rL>*r|k zS>KC!4(k({=P`ea`4;ByFfUND# z_z}#vus)Z$lq||Wf_Vw+*D?>~_+6RTvVI=(I_7E24II83^9I(>XYOSEUCbLkZ7CSigXI80)(;_j_2B-^#p!_2JAzS^qq9BlG*1+cI8RP%9iBWLs}G+1-kbEaO6sYn zRZdT?t#WJmuk@%dt#*plz=a;`fWk-oO@&@df%c;YqV!t&V*f?kt)u-IT6duDYl=&( zM=d}K^zF&!bi_UltqO=s?9(iCw=s9M1=n^fweSnw|TH%PRJvsl7G( z6zi+a_M>ra9ul1XYWdOnYpUBGg?^d(T_y@4_CcDT@oSL{D|>M4C1 zx8f4(+S;x*?Gsb|wcT^#G+q*jH*UzqaqA`4At7*+XkTu|KxdJ&%ihMQt~o_SI?MQQKvweQVmE zYaWkce^A>^Cq3;uX}J-nd652!OYCcT>cx0#))Qamwm-4|M}Ng7_Jy_62jV<}r~Qb1 zBJGp{=_!BD@RWY@d?5Dmn#&{hnYB|5^g9=1?+e`RCAfClLBjn`>;!2uhQ_rKuW@Ze zihXMBl!Q1ZA-L>R3n#&{hahk`E;LY_Boc?O@Y5!9@ZK3Te(=P{Tda-|?r9kWX zbWhS!(Dp|>^|W8&sizjzN~G;4G;`WVr*F+``Dx>ael3X3G6;>>-}vkOQ21w}-ES3M z_vee>0&p zz2#Gc_3uf>8f?R3)MPUY{yLTjD_#YpNcqChiF9t@gjG&ju9>KlxEb?0glS$#1G?F?M;G{GBB^DjGliTt$2JkXOjRUVcnP!`k0eG_IJS+K090 zb`?#z*92~!{ThXL{o@rCYrbv$I&rC8ii%dNkwD+aTQI!j@(QBHowPJ)F1y@Mf?4u z-z5Ls&mUJ&I{CAT_I;z?BLAA1g(}+1kE`f#M7*trf9eqxjRT!3+V^*IsOe9QR?+%q zs*1TmPpK$HzOABuYNLv-F(LaXyghWJin;X*R4iOlprZblmsK>5|3>gUpZyfxSusGx z`d8vqbiJ@z#oV-F6>AKKRV;k%jEd;TcPO6W(J=z+Q&h}7U#Oy0SF57a^__~wcUv4# z>+!$<73;fB7u>#5pub&3DgK~}#z#)7XxiCEou7<5BUH3JGEc?A_zfzW0?Smi6@M&v z?{g~FKiv5sv$EVk8@PCt$s#Ddxt$L*8l4( z6Z{pqJvG>+P-qU*j_RkXZvKt=2EPlZ0{M-?6YPphbp)*YezO|g1` zvwNs$j31<;G&NGi`p6g+?Y-xyX!Bm8qUEa`6-}iNsc5x5rJ{ZC4izy!sOWfVzlt^T z5fxqEeXpWz#UCnK{jRELe?0ILDvzaYXBBOm!c^2t!&Njc9j9XbN7Gf5`Y#ZCjZH<< zhYzS|ZM#WDL+o=Z+Ve|Qbnbp#;IVgAtnv9wMN5xkDmsR^R5U2pRJ4r^QlGzS#&lNE zIU-C&{T;(pyy5u_QS8`1TK@lcNtrhU_?K{V@MxluaUo<>D)bVmA`z*%VEpk?ueXr2s<-_^m6O85C4$nw3n-2^|tkU@cYPR+lRfp zdzN0F`m67xy2)+j%Dvm3|0=qJe8;aBUfH#%oBZdM3lYwj&PV3CYzN~`oQZt-{gM9u z89wr^fAsz3Fenyveh?W*V}#lWKoU|@|<}~^yi2B%99tYKGk+f8+qh{ z+>c(8{pIgAw?A>j*iOFF{+M3Bw3{4tdQafihdRjp_wJmh^Sd6Iy7=ISk+1cW2b3S! zJHV^IJT~du`|CSijC_8$_bjWay=?8WYIDJnAo+pP)qRsHJIbZ=UwC&)UWmM|-q(5{ zq@&#AcjxFq{)6R_KlK0c-F6-2XQyUf-StKvdHn|GtB;+%8u?`4lta%AzD;f$w9?#| zc`-6A{-EF9!`AFZj5Nocnu0x03li_G32usfziZv?d9iwV zN!GIar$6b86zvl#kN)SllO;92a_QISx-{LRm)n0nqtB&d9p$%=SG|35b}M~+DDEpO8B_{qJFZm?}3oI=umms2loWNk!X;Mb$vGkg$KxAtbFX%8IN|B zpZ(Xotvv^xh+J}YO{3L@HdJ1;e8suvpNN!e?VDVq%p>GJ z@nwIs{&tkSXSmnwZx4)+5551J^Te6Xaz(4Y>!yEnm%L~99n-U>j*#OX%zLtLcuUze z`Nu%%o*?eWzH-wezrS+3bh~W-~az%^rHj)=IwJRMPf=HT~u8 z`90rBGY*w4*#&g_loPvs`N&;0vYfSfW3Rg9Bjq0-{4Vxj>qxme{z2#A;=%Is0gg|K zHjI*w%-MgsW`{w3=KP#q)-NLE;jZU42EXVh|L{TKtGDIJ^7*H_p9yI@Onx@FXzoKj z2FW{ikC>S8?)5=$ zxoCRi=Y}8dl4qXIPgoN>QV!nw+4U7J5(0aySLEuwEe|~1xhb;j^$)tHhRO0j1A0viTo@+L z9Zj+L$Svxds&bqQ4~kBpL!oquPhetVF7*>CFtTNr-eWbr4t5qn0;JMZuJ z!O&p?WUpWUd?sQ;Px)s*!@u4;JW95F`sUacQiS}&5^JCRkN20q@Bdn#?^oR+zn8YI zZtC(-*_9mpQ{>bh@{doh%zoc`A#%dB+)p1^bR{zV`z4*u4;?NqT`!-}zdl%Qt54jM zmN!D~{77|u^7PA*_x9V=CFPO3P>yB2k~Uw9d|Fa`ABZ0+hrZCp>zB}BJdTFRldOIl zcD7XHo^hLxza2hW4m3u!cD)!OFYNNghB48KJS=O(JpY9w!!B#GxUv- z=Y0G{he0`$Wb>l0R?fAIm3w|T<=4+jW8}S`uU-&&e?Pfx+1`tDUo^f7@e&HF9}Wckgcc|2%ULJQ)mK9X%ZH8>KiK817&&Qq$+bB(x69oobq?77QI!0`bEbg#A4bVGOP{)# z!^X+hUSW4NJTP4TWVgw)lqiFN|`)n~iN!-y18pzwO6=b{Y^Vk6AarZO=o!Wy zE0K5lo$}f>v8ViX_S)5xEAN&Aepz_Um~yu~_>ZG+`Th_kKhgEny8g)n<<-Ss=bdUf zQa+!tZ(#UEll*tp0T+)?w-F96uG_CE?wD!94>eu!P{le?Y zy<;XlpEMRc;I^^nZ@;ei^;qrN*YdjJ_2{^!Lnp2&j+45VTOPiq{HEKpD!1yIviNmZ z`JInnQ;u7n-qAVhnv(UC?!A$-t|^(FzghYC$ZJY>^#VF|_?8QHlNbFV7hK8wQtt4ilNSGEu6 zdsWGDq&@a(&{gH&+hgaZoxY+3KIff3v*C*J%Llrrmc4aFdHjcYEnY6ZqKtX?@;!YD zt|(m(-HZQMlm{oaIjulBj3I2~)fTeiNU6l{9%kM-v+D;In> z=lB2ivT~oCmvr^OWu<=4zIGQYE-SZP5AEOm$;(Q?(61s>@4u|H_~We~k0o7Jrgmw3 zW$oRUm5+QICYgs{R*vSTnuZxJE5*Hz-Z|O-vJzIkZSRApFDdg1H?L{@`jXPc_HJj} zyO)%!zdba#TlFR7t%Xahe{Q*?%>KbR?dNrulx3akcj}g1QYJl|+%js`C8cw56#idQ z>|Nh|=#BxG6tBsvE`Hhp{&pSyUs8?^)j#>@$&1Rs7`0}FSwe9%bNA_P-zS7V5 zwRhD;MXZlqRIWQ7Z8dn~MJ1y)sOh-vqEfZu$G|t{UR0bXzPM6jzNq{>;JGyi!!IiR zj@3`RY`Ca=UZ`s`wbe!CQs~yl_FZgJl;NjV#s1u+d>U%pr2Di<8F?KKQg1gYWoMQK z@2+T4w&v^3JDzD$Ce@U?etNJ;+5LoLZ{MsYCG^1r{BKg)`R44GrZy?xzV*_or$;v_ z_TqKANq0c6UQLQ`s5MvT+oYJ*=&rUndqL?o^`2?BAG@HOZE=6p{Q3*ZYTckGUViI> z^3F-8U$4pwO2LTNmXivQ}Z%W~RYP;6@h=i;r|N=NJI4%XA>l|#d$ zmtOecypmA2w$IKZ=ar7B`#PlVJFi4`-X1XV#q&yG#`Ghjx1CoU^?4&FJPv*G+78KU z&nul5>xR8>?|J27uW3t8CY)EcPa3sxYV3I>v1j1yx-sXK?;H=;&K!DPF)h%oxOCfj zWpmY?$uGA%ulRO|x2|n*Ub!-`UwGpAbILF6>ucxya!%QC?BqL(8_p@S^#AOi_ue_> z@k}p(v;T;5%H$R$Gq2xqPHEqC?AMmv&MEI62>GE` zFt|mB|L2tFV!mDQ*qO7+h!%r)Ie$8<#0>c6t>}icN?P^p57oYZR{7zvo!`oDo>k_& z9c%xh`m8cZx4d#q(OG42{YyWNc=D{0Yu5E@k#|P=(&Kvv96O^N%r1NU=;vpYU&`+*e)7FD%8Y)u z9ksrBMrk+d#kQkfJfrlgTNTlJ#~Eeen$Q7VpE;vE>bL0AUYnqgSlc#Y?HT2daqk_O zlYK@pxBTwRrj#>E(;Y9&|L7lQlzln-$go@wPL{Yrma{UlDpnDLi7-b!v4+v3ec2qfU25NgllI%?;;IE0d30+jjSF zruIZfx3Xtfdke~x%}Q~C1m(Lk6wK1v|{S7tMqy0w9;bVnJ2oIo>rc3 zbG%>F^QV;^KDBc){&`wyTa(wOChxQ|<)dv8m+n8U+|_q&ttIoc(pUGz>X(yGE2o~w zwe^~JT6t;3;}866`f25f-V^cvv@*xq@#)v))5`aIpUs&Wepxlkb~$O3@9u zV%d~+Mdw%yhrzYd`y8Y!#zn^m{gy>-LjBzwT0A zJvk_2aJ5U>rB4o6yVIpu*H`cS`d=<3&R;h|dD5jEOUiz^{1KP3K|jW?=LYa!Yx})$ zpG!G-VM0KkOqX)G&C0E>E^#SWN>6<D7;cN04to;cB^ z=zXH_AK@mfd;MU9OX)nX$E%MUUCN1mE3PDlxfDMiueBzFOBudu+OXcC2(Ml)B^N(D z=i}$1bqrh@Vjnww@tsEDss0uF7fLkaJNVhzj76#GEASp0yrX7edScT2l(ej*#o7!GBC#6-M`E7O3V{uKo!C9ZUoCVi1%S<JALTgSZc1=*Gx-To`B@>O08Rl9cdUpVX`4AF~OFVvOLLP&R(<# zZ{qon#2Y(lmSMVh-IhDne(yu=4~hT^zQ6Cr0tkDVVE z6+2;EoFOee%aB6h=#^Y#lXPkJLhXU~hb`9njk+Ecb$h1pTgUJt>NnVy+iq7R<7eK* zkU+7d#DuH_&wzM~(7?>3q@}_TwA%0xLOHmCizpGwHZwUrgK8?qSO#?y{eZh_k_IhP z=*d(w(h^b)Nf{aR_Bdg{D^k)DF-B99(iUfl6G2HiSs4iiwN#uGBI6SNS}QcGvZyVx z44lxyU4`*XV?SfCRz9SjmWdbrr1OS3HFkmltv_-6c#m$Q{&4%? zon0B3R4LhDq<*A0nb|fQYOZw;#t#N*`rJCi+ZH5X6#|# zT4*uTEl9@{h+ofE=V(kxH}t1xirG|&`C?SzTozv3r}erz2B_jmiSBl|p+E4(Hdy(s zs^M<$e>r@R0!!NOrtEj{r{ypaa&UWoCJsX1j~3cb!x zq-IcG2By}cIi5O04dws~5JkH&a<~v5?_eKaADxen?{;tBYH!~+ynPRO`+n-}`@Of% zZ#thpbv|cwJ{NR8O*)^8I-g5ApUXO*D>|R6I-hGgpX<7?o#OnpvzwGld;k8Nu6BgO zhq;E@af&wVe;cC)Mo!Oh+l}ebnF6}BbOs2jzH^A2_b}JaO*54uI6Zs68`JaV09txl zd9{3c|4n*&PA|8ONKeb>|1BE+rv0=NFG^U3 z8WQbTaDF}^waGk)y+$Y5i$n=DT@d`+L8KSqzx_sCD|kPG$nBtOcayF)#ks|0)GBxj z-T#i4U%jx?o6`s#BK*S`Z`MoyN#*%VKL3t~fy-xPj9`qNK6d)=9~Hd0HUHw+?a%!= z_QcZ4K~&_iBj@JezMVRMh;fKZFqf;q{PNy_czXSyMlqWP)Dv>uoejx$tu$qVU6M zXmoHWalFzZE1|dgH4yXxeq$bfM&rUocp@`_(|8;d9UIp>J~|3c$$X=qI@~Qc_)S#9 z#ltVu9d}|h#kJiSPK}#$gP%o>n+rcWDJAl?#8ceD8^gs@z%4iWsp;8o@QYK^E4k56 z4Oas{*Zs%?FY(Aq5MzoCO9|e4)v8sA3+SQL{xY{gM%KdK6Qe|WH~8Hn()09-6X9xa z@Dt%2@TgrC}$qOXRMpY+ZR{TEJNq3`OX zW0+Vt?lAI`>e27G84ME1OU|P@$tj< z(p?Shqye?!l1Ehch{!)uLKdEq> zBprkpUZpZ%R6{R8FF`N;MUV035ndPIBL#%FlmdqM+C=$$C5u^-eu1o3)1&g-?1zI8 z9$|DH{iKe^(as1L5bh_^qI9!bqQ1V8X$n4zLn74r!ieEdY2N6MImshjr>?$ItC##; zEp5Km78V~P^$ATDJ%fCupyTZ&zt7r9J_p)LE{xE@b8!EAO|-u!@6^*zY7^I5Y7^c{ zYLn608DtBzYPQiSAxKI%8zRjDfGCX z6a;%}5$B8Fl9!~PAX_+JPu{6L?D8cqwagYOb9-N@{qYc~RhAy@5iHfDNYZ6UJ;(Lr zy6!!_U^z>WfUM z%=KtiUDqI~Yhr7uYg{X-YdGAop{@?jcJ*x>7>hL4oL;t`ef^}qXMLp}i7lj#u+J8- zPx~soXA+Y9x@bpFu6w1m^vYQusrq;eX?tRz^c=>a^+1s1AKRkd+va6d$8r(!MqNYN z`$_FF#@eHu+GBjR9}?m;ZW8ykpj=714?_1Vy1s+^1VrOJo-HA&O~Q_1eQgw%o~LeG zD@kJ@PjlQ*xbK0~Fkc9Fnunfvx`$pZBgUOS=8DeY!BXc8w2G~*mD1NC{T&Efunt28 z=@F`BiS@GR{BVyzJe|Kz@?QW{%hpN?$ndqP<1#~xOC5gnyA|f9RzrL({xrAs!flw- zemf8E#?6>g z5sH4+qo0G(&*<-r=6?2XrIT8r4qE=*3h|zJNSix!D(YH#OD)HUd6?4k(s@f>9VA~J za#7RP@*pKHFE1a-8+tFR&eohaY%4Ahc?U>chcxF+;kCB@Tf`5NcYtkncp}*1&vUp| z)|40UyZDg(D66*}_7v>np1-v)1EC)VDM1)DKm6cvTcp=EHpms=6eB{{ElBEyanudt zs2j#nH;kihLpr%SI@>#R9%<@A1EkQzASo1e2}NB(W81mfI5AcNby6Vpx2lIf(g;Nw zp+kiJR^{5ZOOnbVTDep^7}MMj*YE{Y-*VB$8tMYPsK1Rme=o`Z3D_H9hL`15>HknE zNv9xM`nL*`P=)m%$iEe)6#Aq8PMDt15B3P7_9fb*(;&2E;sLbhJ5o2@Tar4fTEzV0 zY=OG9qi28sJOlXS8368Bv}LCNsT2C56Z|{DzZ3jB#kO|}|5or1fY$_zbDDE>;TUvgLYd zWeY;t0#P=UF+*?bbQ>_d4Y_ZaQQh|dvd;`JV~F}J;Rjp9eGbnOnk_#00dBRBZ#7%& z7@9kb zs0%$yWNFV_tx=~|s0Zrygx(f%)0mq171l@~=e06+#kkGti2FnbjN$g8t#xgr_D-a$ z_7(aF&q$r(TB0tf&ye=co73F?HJ)D}!Tos*w$x!xL7hVUq>%6sDI^0=X!ZUMx_?t! zq4}k4-(v0cJ4w2o<9rJDZyx+s?cwzeo_!&jJ?nzJq@auzw;Ue}zL%sAAmcgxkT$-! z_v@uLFNu4<)@KsxSprGW!nMP*YGSj!YT@4aNs`V$9?-(6bHA9MwJ^V(!1qTWTR4pR zEU9}~@^$*udpX3o(BlTyGb}&~JG)UbWIc%a<{?QRmnZp!=SzjZq75L2-xBv-PcHi2 zAAJwIiw(4OYK!|ykx0`dp|8~P;5iWYqD})_-PI$jozx|x+AT0K(w@k21H9 z4RLAhG~tRQJqmH#Al+9N+By|Z7l)4P*=xXn5TOZ;Gc8|fX zNNWn@0}ik27=SkDD0O`4#xdRL4!U23NnLe@Th1R%sAs2v{!*8?La#32k9u{{J?tec zhua(<-a6fhXRx@Zyn2Lh_UfT~)T@n4?`-X$Crc5=iZC}nzt9L-7l^+>_zb6&k;Z5& zo}H+yaX~0=pww|lTUUrPxW1JGA)4hq(7x)#nfzXX-ud#<{2l z_OS!%x!VSx2AId|xCx!1SSBx&Ut}AQ{huu&G;(z0$5a=>$aU2c4gn`k4@hP|c?e@)6nJ@R#;B z$)f_Fze(@KvIX1@DS@~ikfcJ$UbqiIC?#Iqp;eth5b&Y%#d^Ey6k8++NuS}h>*RZI z8=JZ2uOWT5r)vP?c*c7emosi=EMeTs*uZ$2(eI#0uRCJ|=I{ZBt>I&5vzdGSL7Fyp1w5OHabw_M(|3I3VDH&PW38^#E zXk!SL+hRsqaza{SYEt4jZ8Hn6rpERmK8dxMo|N^s>TX_mnRIkEw#(DBcS=jow$NTH zoy4!1mZW^|iJ45&GsY*;n{%N|%0x8D;xjEfDPyGtoAT)y*mz!uT4bXd8B+JZram!I z>gC}ZmrN%D;3>Jtclt`~+boMqS(X%)i3%ign1<~~>^-kanjVvqnPo;4X&p*5 zHfcfjVjLyVO7f+e2KK|UvTYO77NyUU9w477>518?NoKWy@D&6nwV-G^ym#A%)KOO2 zB1%depPHSStmYvtrkF7}iZD*>zADs?9{esHIwUD0Is-++`?{x#OzDB(i>74q9fBPi#U;W|G^6uzaH%my_bQ zjQekHO*Pi4W?$;FyYb?3q!sIpZnCKbk?vOAqVV_W#=~Z#Q`0k(q_wJhd2Yn#vmZCS<<8EXVfeuF&f*umIUnL*XmG{sA$}Yo~N9rj+;X6L*yH6p>CJHQQhgw zV2r3=T2hqs?M*(jq|@G0vQx8C#;nZ3Yu0C_Bql{;e^hGat2yJ+-Fiu;e8#0MPr(rq zI$p;HZ+(i)@gQ{tpcPv2QERrQVal1sEkT4s7` z61ANd<%Ow-I0j3SB;83VQhZCgdJKVHQ2&7xjln9Cm?b?ZseQ12-w%AIfAe>pY1XCt@?Zl8qz5!y+Pzya5k_-sdfGGd z^Yr{raf_2xxB4rxGyLuKpHiIv@8}l_b*xxhj?*{J!m9Z8r$@ zU;eJeXI;8XjIaI&!$)!W*AV{WqT@xKrJsr#i?A7T)8$V8fo*DxX)Ppa+E??wGD!2k zTJqJ9n?g&{+D{w4b6&(;|A2_;iWU3!;wkvX&r_P;UlO^6hu@dCJ|Cb3{XebO%Auj5 z;nwA~%@F>snF4K-Zyioc=kL(|xrk@27a09_0pYJR;Lm@;&wn-M@2r-E?{LrPZ7Ii7t)PoRGam zWB&ovOHvVp&QDnJBhHyvv#Em{=f5PJv68-ln-J&UR5#(Pw+T1Sut`t$6KA7>|8_+W)`Hgf&a}6WSnka1#baiST-0`558$-0!Dz01ika!V@+@uE0&$XDs4l z51w!Wy9u+{P1tLk2t%04Zo&uI?YY-~5Po43u+D@u35S{`DF$xB$&ggIJ@@tL48W9$ z*pGle;X24xxCy&Y5^ll?>?Zt~-Gr?ti!g-!AT@|<1P+;kI>YU`-%sZQKDb+wK7&8u zZ;+F469!HdZo;MPChTDm{)E@qP5AV5;ZHbehHw+U!)`)lrtl}UvYXHW@xi`4;jj0I ze)l|6KxYlUn~OF?7{Vw1fpb$79#}OG;o+_UE}1WE*aoC824umXFd1hC$d)|M3(z?N zV>0&N5r%LS#6jt#G@lco^8y>vFb3dHXop;ZoA41EYykWAg+TvI^fBCeAbndx9Y?^% zY@`YMcL6uALcb%7=N>=p{TJMavcjM6HAo@c4&eO{AV0WiE%YSB0XMCUPFy40R^VDl z9sD-{KZ7LJ>QwwS7keA`&-qH(|d#QT_qI*V$bQwB+Nw2g*R01&Kg?3IE0J8sMde zG5!(e3NWGopI>+5911Xt-8sNc*h^~%f7(|W^_V1u!#x_f2tsKl0}CNkU&3N`6TZgo zTHq0Oe+E3xZo+AWC@a#m0dq;O+!kOagwn|Y=CPabU5MI8Kz*5T6E1~N_*~#- zb`ut}oA4;RzX6_PH(^jY+Mmh~TneG|Q-S*-l;#28{0g2|fxknV5WaUMY!Y$>?nK~z z2*o`B^sa)fyn+2xU=$=5?yP6B1qk;EBD6d>#+)JWtgqI=Ye+5|gvhXL=y&~%31MI`@FrW=W;U59M!)_#M?_ zaK&q|f0Q8?IOuiEO>i55cSDNdwg8`oP+K{HgK9-NjX(>8{0VLBCj1GamJ^t<2R4s% zvVhm#K-^-zK{0o7xAD~`v#{q*5qh4?mrb4Lvgn8^HjQSWhjWA<@ zyCGfQ#JmAK2chtB^_T}B1K@uW`1=u2FBfp*C!!2_z4{(0Dpy0IwyhQ zp9_D&RqS33+|TXnQpRVIqKIzry?sH{lwH1@2s+1ES6eK-1SEU&7T8^4|b_8AA1X1=t8FMqI*y z-yjUmB^ZJ8AO|Q-;K}dM7nIlc=u=2I!Z?8rC(?(Tu-gx4TeuCtXCYMIt-xO(l$Q$_ z{G%wd9=H@j{u_W^KZ!O80!Bi}-vqpu-C4kAA=D0AfdM~b{35SV;CKj?(+pezp)f(e z;Muj2?Fx7dLgP2+1m-yig&{Qjiaww+12;mbeLT-&&^e7Bf8c(JFocm1zqe5~VB0@M z_;$dTAYt%#0PSb+OapfbaP(Q!1#ZIk&Y{oXJ`DV!3H<^0ap0hYLO1%)+_Lb`y_DZRrD|1CBSaiMEV9`1>_3+ z2_vr~KHMgt9((r54%`QUZCYR+g1a4Xps#Qnf%cY&2YKeQj*j{q0p%!38)WMC(p zeW-`KD{wA^`jqeu2-R^fuqDnd(mdb?jDnm+__4q}tx-R?_W} z_gr8VgvwR}JPe^S6F%G(^@IN-z^jlA2#~iDrX(= zCkVB9Bl-1#?e7=39&YmA0DKZc;a$MUzGxGKq4Oe}Amm>N{0%~LrWB@=ra|H<3~+cq zlm%`JFcuPqwzUD5LyU0K8DBcP>xXm*>D(^43F*u&xe4iv>}I$L>Ab27ZbCZ$WkWiI zbT*X26Vkamaud?oB61VbxioSU(s?s-6Vll*aud?|FLD#o8831Z(pfHY6Vlliaud?| zB61VbIUaHo(s>(l6VmwIPc1!%aoL{4vjgN_$Qnp3!~v;?I3YO@{Zo=;fY{+S0-Jy)pcV2Y z-2Zd^pK1Z|eW*~}(r+vQ&b`9Jcng1jAAGigj=sADbNI_OTFF_Ks(tZ1vhSUPjC~E- zH;*Ix&WIa7FrqJhw*Y4X5>nICk|O)AOv>!5$gKiKC*X_4%NC@rG$4Vr%*ejk8EGRk z@x}OM37G?zr7X-yr=L6+h+iTYnUJ|`(DFO`((fyzEW($JXNs@6i`*!w!CG1%e<|Vr z+yLG#5 zd+zqa?e^_8+a25Mw>!7HwoAqOVnea9*i>vOwierpbBha$?Zq|4j^g@aXR)hT+M(ZJ z*kRmZ+F{vY-C^62yQ6T2eMij>$Bz0P&K<5D(oX$O!%pK)(@x7y>rUIw+?|Cx?K^9B zI(F9YbnbNRly>QN8Fm?WnRZ!rS$ElXt;eW|llGU1+% zHYl;z*lX=BwYH(CZy0JEfjXPF$D`KCsCN!(o`<@ZY_Hv3hq^bQ_Kn-)QKw|o%CVyk z^=Uwj8d0Yv)XER_3PsJrP`3!w&W!rS@3fWYl;@V`l^2$8DYuuGl-HEkmOIMp%InJ; z%AMtn<*xFka;d_vLSGSDVWrLi)i(o|`#v{c4dS}T()ZIwBdxs`d9 zg_T<>?Uf~!HI=oMj>@{q`pSk%XJuohtFoz5s`9JSSA|v?s=}%;J}h`|}&_X3>ojSBkBU;4|EfR*-Fry`s(F%EJffCfd4s~xtz5P(Q{%lHKJaAs8d)~M3uQJzACvYrz)>%OI1l#ZB<=WLser{Q?yQ#IPciswUspFPwbW{#4Ih*cD94~6|jsJ533yUW|Yg{`gu7T2`huQ;?gtT>|BTpVAVT%1#!SG=XT zq`0=YuDGGNvAC(&Z%62kupJRQ%sb*?n>jo3c5K;EvZEFj+5qcp+TjPg4BLr{*zj2i zeQUv}w_(&57TJqxFzW07FBtc(QmIT|W+*e3naV6>)-qdJZdqZOy{x9pQC45(EOV7f z<@$0%xv|_-ZYj5x+c0ViF=A^lTI(@VT^OZ$j8G#+rv)R^hEZ9F5m}>-Mkhw1gi&a~ z2sB~zSuyf*G3x9XaSn_&Cq|ltQD&$zR+*|SRn{t7Rc=*bmA$H_%28Ec<*ag5N!9vl zL$$HmRBfrYR@iTMDwTtXA7oWEPl3i~%*o}6R-D0=eZT4I{`HAnH z(=0%vo<=#1YAbBNhO8cGG*TKh_*?_XvlqgO;i$)FKY$&U;s+}+?@Zpg1+#H2=Hn*J z#Rkj)M$7^x%mWt81Xgt}$i-|>i21;d8KI^G(f{ZAZ_xtYQaH+a{d#~jUW$^Y<2@$x zr5OC)$P}<4?iv^+deRfulzR##Z#c<=cAjh5-^BQDK`7>$);;#%ORfGN_N>*#eAqKC z89zm2%Sd0Gk+2NEBa=qIO0*!!kdd8+U%D}jojTpH45uFlwQ3d6n-iWtE7iNpFfWTH z)n#WaJ;1BWPFWsS(PilX#JhW^byczDwAL#6P744YeD6I=D6q>gZ%Z=p#pHl#?N!uG z>#U*|<5i2`lFDOK7A8}ke^)=tIIQVB*iyQ9@Z-5!7?;$`5a`t!3uSnMrUxxtU6%l9 zz^S7bf5{ECFZtloP_m$vxz+J&0q#f~7a>I=8csjw;7;P+YW80hC6?&a7pS3aB52lZK@uAZTd^NG0^`1;xgyY z?d@mIPwlNY=V$c}HRrGHZHSs_UVqYJE;t%haLQcZKW^yxi3N@3f=lL&?`8R#M@^4d zzOXSS18c(tll>Z_;fTo}8k3*t2W|mIG!9mkAH!{F>`(4|Z_>w%$PCR2G8cT@*g^G- zMvOot8oc2Lw56&Iz+b=M4M&LQXQE6?4Ztvn(VTx@1kkkce3rkt;Gj9U`p|DJXu8TZ z=SQ2zj(DYaE=cf(4fu;xEZK(LMmQVG?xg}mTg)3B*{#e4A2ebYL^|;#{tR`P^T+lM zqd!P}Y;OboiJ(6wh}m2aZ8-#e*x1{80|%^l|F@07dITGbmNLR8eCmUB3Bmf9-o{{j z5fBw8PH$otj3p-j+#js97C_0^GBiK z)UKfhY}`!h{QJRwWF8f54t|K%9aNLZBgdvFy?Inq@H*AEAUf1s@TIx`5mZla9=gxG z{+P?$|58kW&j;p$pm)rpE-g1s995Uyp9*#7MDO~Tf0-gT)>#Y_yl7armF6$Yb39U(m+ zeIbJ(!yyV}0%R&=7GyqTF(e(b0cAN{^!nI> z(*+kdzL%|Utcby$Nx{b(9l@LTZ5pkstwVHJ;Dn}4Q?HvhO>2o6aZ|fk)y~cPj`=jk zEhGDCz~qvZkY9_93u)u^td8auSjv|QK3-H9lOJK;WWq>0q02sVa6L^sx|sYoutSB> z{)Qc3-grK{#l(U`zhRJm)OckEeDc?Cfnxo2-Es^tM|LpHT4#{pP{)ZN; zekg*AbR1dV(M)cND)@8b_23PgDe+A&8xUi6$wF*d*i%tf)NI3f(j1a-cayHad&oq$ zcIl8Qh^9;PA(K7|JSIOU6mGhsLPfO!44{B9`L%u*C}6H0APXP+e44##YX#H@r~{bu z$K&?W-Ms#wanzr|>oRe?F(!W$Y7m223AGM>(g7DH-RMxDWo*P7*bI__H@<{)LDV`N zijG%{5p7iq8l6gzHTF<+VhF+>icZG!5x|BgGZZU22jFEsw4QDR^u%>&{Ui8`{W$5j z$!N)#{0ICRcYtA%o{co9)<{FGVYJO3jMLDGB-A|D({!W;Q4_m^;;hfbU!g68iyjyb zMQ2ft<^s%{=m?r2a2v_$ZK0VqYJSwbn`T>#?@u;O(w$gz_4;*Tu|q#OG22bxrOt)F zB4;vH3t6w#TyPN+J@s@l8L}+~f6R?<;6c<}Fx9W|B{+uuZq6TU&QFI)O+}>ih{mm? z&QG@#d^@6=taSOvMZrDB!bUNqgD|yYhJWQ_>SmZ@Ry%XSqtv5~i_}CvYMckW{?4cu zC}zgtn1a{IKBpjL&csc38)NX$V7lIZcS|n`)EaeYyxN-A+gA%8quzaSw~Q?>+e$5=23$( zz7XlQAzx&D53)8FRMW^cZ~Q$gBBmhQR4`6YtzL<-hYn>SBj?)$0LT=0xqmV!&c z`n4q})#+e;el2kQeST6_*y-R9JeZi*ukLM>g8SjAC2Lg7rWicj@dL&OjW3SDHe??) z9PD#-Z;Kkvl+~8PnVQ4-cSdzKUeAsy@Q!>RgJ3mgwDmC#e|mNP`;o?j4Ry3O*U>Z`c5O9p>KcnXqn_+<-;X|K@1rrB z*3vSidDGf<1oU7QvuR?yIX@0hTl)1z@+^3q8UWQUFsZbF>^BBM!(ukk!x(C964k@Q znt@8wxXFu#!Fn@=M>*!a<8DvG(62GD{vYz*1wN|s+WSv36NwPn!J zQ?Cz?AqLjou8*MS6#Bg{*(&9P-?O*9;RN2ZQ*TtoB7HHVao>a&VqICaiREmrQjY)7Qa_R+`buR)>MyKl82VmIlu-oMGfoAqOA+D^5~3+$HXuj)L1%_-(D&wP8}QADz`s_e*S%l&!H zR%xvI<(*Q!(fFq2K2oz&ZZU)FEiZ;}C<}<(DE}FJYq{|%ZqBvb<_fE?IYa}XPrNpJ z)St>EktEN^m3mCtXeSPo8daJeQPl$^NwCXRanb~%lo>&vM ziv=|CYB3QuAk)AqX`2U=1;kZ&$PT5^lfh`eH;4tSb02}$8*d?KN`>l zBRImH zmd-)Qz!MxbK=n_`yAhL+`oxRevOfUL@mY*T)s`Rhl7Y0rw zB`31wTmG8SAhtXfPlr&i84+;VM#PG!t3k(|i79)l7PFUiXd;((>>(L9j@$<3esbB z|5{NhtsT3qNOx=VT2Xy-KaMvKB+d4Hp_W}FTOzi^yE~6d*0h_$Q$6`R&7Sp6bJ+&7 zeM9NTJ{)}T)ZqFj01s@G*_X{i^RT6qunbyS{M4vQ$g*g8(-vcar)!VVi>UuN8UH*} z|6Qhj(K2Uwvlff>{ra&&pM<2uest zHC+P-8LPLVRcmq6JmD{nd)IyB!!VK7%RiChkXe3G2{4Eg*+=F+E5MXW zNaq9^!D0!d(-TEvMiZY0p0EQ?Tao^S zRYm=WD4AiFyIzl`%5~T;t~4RELUI~i&qGNk{7}s@ZrG~VVIHA-GdR+WoOQ*$1)@)j zR^<nwjB8T+ z!}$zckv0n~A(FsRn}sDqjw0)G^1Ku~QdnluHPgK@U$?wTtUK?wo~{uE(J(fpZ)C|> zHq6TQMVEb9{?`3WN@_cW3n5edi%$4OJMn(7_3#`~GN)MT8Jn*BW)dNR{q8XMB4~vV z(6HTPuv8ycNv@*HekRjWhG>p-bf%2h+=FN;Q&h_zp=;Ga_%mzPtB!h8IW_YqY)3ZR zYESy?*JWJxNe>RQyeaQyPF846^jz!w((z~Ycnh%uBEr=cu$lDQJ#xu{b&)X5RRyMz zw#fh&gR1{sd-83;YEM7-mrviEHW>`RIph9@=3r}g2xUcL1;J$(%R+&ksYgWVD!GI~}#5?I^GD z?Zm#ag-0;ztOhWVs1u3zMj1<|Q@nLqH^^bnb0CMEOIC)t5$MkDZk~{g)^!M8!y0U= z{x;^~`@@YN1Jk}=YTT9_(cdQJu@dIWL-5>%$f2y;7W@Q_=?e#^k8dqXGX5?AfId?L zL1v#hS0=D(tTtU!B5JvwTD4>MIxZTxHD5J`__|6pR`RvLsvm9{jl*r%UTpStw3Eos zU17Q%BI;G+7_LOVsK(J;iE;wJnC?$j%9ZXt=9m7_^#Y=E!uDmlr|HKullAyXpa5O? zJl)3v;A*rkfgux@*#yo z&#o6t1}EwdrC$uRDl?XL>hxC?(sP%df1kU`yvPdlxECS__5~OIJq)^kkU@1SOQ)Kn zQ(dG}MGukcBwfSY!5TE5Eb9&8r@9;UEd1&&viq8dn@)KOo+JBV_FY9)^@*_AN$W(W<-+3~# zJ+L+Vvd`8%EQ~UBk&)*t*4nn!!A6Lxl7($Ks5>-XXEMxJT(J z)T7KcF59IHWiH?|pU)gV7x9_PBN<&TFXvKb8Vjm;5^d1CNAkbyCW{C;KV zZ}sYs-?{amt)@x?u=Dylszux|e!F@*0LEmO6I#G!-Dm+1YBj%gFXmq6m6}+QvG%$iPH^Jrm>a>i%yD< zvN}Y=3j(2oH#AmWkC$L$`b@TYpizmI&``HtujV852=YrP+*T%QP!q!BS(JK_9#J)| zO;lUalaW!5@jx>D%1dGc;KmqK?B@!ME&34nq^~@5WA4k6I#+ZI|0!+~Q~*Cwj>ba2 zDzQOBTEn7;r&)=0_fm05yw&$%9DXL9brW(=R`!dFYORx5d`VB@n(C5xGx>ItrX-F> z6I2sD{a$_vrlIw%x1<=YZ_Rzv*SGGQCEt>?PnRy4-BqG!A*pG3uJj^q$mt{6Eu_sM zb*m(|lq*%Gjk5&YYdew_zNtle!H~Q~BFn zTlMdz@m76Pt&!-~zYbD`(|(dF?d5dUeg033Nncl1WX_ojxSR!6uSP3nl&hR@Q>~>& z_gH~hwdr#w3N@yU$9Aw(%92N=O$|6QYundro{pczYIdx9U5D zBt~KbfAM3egij$oLmY2(LoBd;s#?CIj%w@wzM|~}Od3l)S!~c$b{&at%L;%1SRE<3 zc9d^9dX+a@>V^s{d_&b#)wx5=CEFW)xGiSZNGmWMZ*w220%l@g`YjWBm9F0@Ei1hN z;`^p3h=Y&8^hj>2J|PKOP_Io8g8Ox9*KcwpA-yt|)Wa%yilBYg$y{j=#=f*?Ek%@M z{#Znz!TADJl05Y-6Q6f9RHGWfT!pFY8>@`OTRO?6bac0#vgaqRWvU=ge705h@;E&w zB5Z4P>BcE-T5G5B(#<-vZYJh=y>7N~8yXuP=cT{Om3#&z4`J^P{XqQoFkX(DmO45| zI^jDjaDEje)chKlHa`9D4MIw2SSrqUaz>iQ$3qmVhuyEYCG3lpYjal3q7#ZLT?F|j zS6KCvtBfThbL12f3{GOJFXc~3eVgGt^z;TVJ5TdmLE}yPxCc(wzT{q+)G{y^3n;)S zO~6fwmqr|1Gh=FTl?a>TbL{G4ylI~eiyZe%F(Z0->hGnbdQBK0- z1WAK&TQ5;D1*cMjc*$nwJ2S{&n!k!Ekn6t4G~N9?;Vxx)_Zd*jv_I=Xx8b^Jb@Ai) zTiZI?AFz$G>ghBhyc{JWXBH9(Yut%+8%w%$&S{|ukOq09gFS=;Eab^%QpCS2B8wmr ziS^jpav?!N@!3Ne_{Vix?#g=P53*SL#SJY8-$jJo_*fAULPI3)qLYM>-}{{~GaM=b zBe16B8t@~G>`oD2Q$q-Y1w*zRzFK+?6%t=V1Wd#nA3uWCemk0?9^ua&Qr71XSgk+! zQm#*|4u&uVz!<+<48Ht4c?dunys>m)uf-JSX zM?j~VM7RzDQWF}(^Rg& zydZTcQETi#N1Eh4reA`oZWZ{~cwj3xB%RKcdNo*-c1Q@0KmPe)f&+X_p#1B4q{O1t z9I0)@VuAIEOffhepK9=VBc~GKfiyb*h4%Jowe%wwk)^QOBo2Y)(W&kp*Hf;c+@44xVW_ z2hS{E?N?sGIrWTW&K~tX*D^0ywrD}$QgI2$KdW!)Zu#aPmq~pA8oN{j3I6IP-9+h|Tt%_NSgn`K=WRCr8HRHbb7kFpp<1%%twDyE$*%AgP}XHAnOSM9I~`~u;( z$`{iDEAeWX9q6{Uy=rB2n20;-$UkCLQjqd7r$WTt~BL>h)JPRFP^LYGtmVN1pdKxuT`k+V6y1m|(nDjb4Iupd+U=5h@<^UVg zVYnhKMNj9Pu7LI8lf9s(=!GeFuj@5wvFO8Enf|^0^*^FamyeTKaVTXvKR##V$LCbM zqsJf`cI(z0TA6H`V2iBXtD3r+7ZNu7(>+n-A|dn`Thx;l2F`Q z@#AydjUP(U`Q(n;7`oEYhyCI$`n@rMsvDDCbFO zSK>XCY+2iKg9sh-3QM>D@OB=yuh+eZ!$CefBUm8Cl31oUlDp?&teB(qAcRjo)~!Zx*$@Kzh1!2-0i%(~`3i`-d-Dq~l8~aV?eq zBn5Ll%&Sf zNiC!6E}v+;-l1zN?oXU zmCNtDj2yDGOFCInwP9N5wP+6&T1Qdqv=11U%&qOT$Wr#=^yeiIG%Hm#0Ofa$r#iG~ zX+MsY_R z=6Fr-WsbJRoiV!I_9~w>)sFP>2r>FpMUqSyYofRkbfld%8%HNV}?s#G|)YC&^#I=?k*@p_ajAVMHaPMEtrC_4&{q0;4Q)k?i zqI}4idg(B=H*H8$+=L_}#!mHP-;{%85efIsm!-2xo{&Itqx&13cS-9cg8Mez%ww(k zRZ?B+_Y3~Ob&@5$`Yh?=?eZ*NO6ujqEH{Yaul&2ZR%r(SVW})2jh6fO1iLKpJKR{? z+Xz7#eS&0luX44u+@*&>PY4KAL_=A+N6<1Nt6$9VaL#_lc9!55I?@49pCE^eQ74%!3xw>Ja6cW= z289l?v)u1f&G$o&`~8ZjvHG87GUyN(v;B`}JGm$qJ940PE-}{CgX2RF_BjDsISW*9 z7K1UZ7GsQPF;GBvmNngjCFzcXMM4DAf2Bo#@^!208F@!7uW5&8uwsbHZlL^@UlId? z9SHfH@w=^nGd^wAFW12#trCMM9U368neIOBPn)a+dP;QTvAY;HulyfGQ*}WUy%}>g zR?#;lBuwVGi-_&|V5cOK{r(>O>+X1k&Dx7L5kCp3$1)kTBjz1(v7s-PTdYQ z`JiDu)&ab9JinEeI^Q43#)&eLlUZbL@JACypjR@Q1bp1%H~s~Q*Su24YvwHb#`-ID zyr$UZQ>3v&A0I1^4>ISgNtPjAQ?i^aS=5sfwLZp=KQhF+SaiA-7!coHq7N$fwxmfT zAsfsM*dQnEVUB!|O`c)&Kt#un&uq`lcg=bdrCl}a0v**XvX=rnYgR(3W}=bR0Qz{d z+Tkd+^Hn?bKhaY=ViKdQk))5@*LfU*3a{j8HqACq-P3gP+celjOsIuT)9fzv`t4fxmFQoAeo zIX!3wdJ?I_c-H=2psoBdN+BX-LwtDRKv{g`0ET=CS4QWN`XXR-9m|DSf(*~pY&3*k z)z{b;4b@4;{G77YGY;W1ixJu~*F)(PHbu^aGnU?`Q$`taG#!L16Shjm77p--a;l1c zdF#|b^vgSK^vVMPqw`U+Sfx(QVn*2-=TBTTs+fA+Q6Dy|O{RL^s@Frd@JvQK=TFYb zZ^j!ceByXp`SFfLwirt^z}}a{utZ(c)`(t^{P9N^Hi$?wFOu*+goiKhjn1q#Q~MlZ z(}gyrPN;fn>FiJ~P0rl6R>+h2RbKkuCoFBO?yfm|Qjr3->{bzd?2Pmo{vdtF=Xrl) z8OzAsHSHAXsF#h^I-9NDBnT0;u>HMkd^kfpO18RWHz849wOl`S`^#q&EJ)0D{eS3n zFyz;0<${|DFqFjtQM5UI3+{u=(*OWjyyg+DagKWuOLq8$GQT1j;8 zlVwJ|WjoX!Nkza{-uj^K0D zQYl8ZKlCIX-^yjF(ItqOEj}f0MwK0&T;+JTNH$v?ZR5NW3NNs{C@u`H2rQSMLiO}h zr=r0;{$C_zf+3EG;cGjZT%s{cMs>?to~8c;8Ng^Cc#t0~^fe3Y(#brWjc$>6|FuPD z@V7KR+E~3*N_uza@f#AUpnHq7E!SFtxmw29;jLCFh4$$VP-RJm<)>$0Iy7*?2prvJ z%s$;?SEsT9&!xY?;e@O?RCs=c_+UCUc9(cu$iUt@!OshXxSt=zgrH7G*0R|(b+JId z^bE!mnd;NUzZTN4ehWd?j~_}9e*zwgCfkT$9HdFEP)!yoOfDdevGjNt=i|vkm+00d zxNx7(bs=s_^KE>Ctst52k6v^tgP{2)yq`dO;!j_+0!I&Ji=*KptMn2alJ{uVND7l8 z34dPGPET{v;}WDtacH2P@HxC*nW!>srl}A}9#1@`U0$9YYjsR%7|6b#P1jD4wRRH# zeVp}F>`8@}3B;Scctc#PLe#~icfKK3#lzzn0N6G-<1g%iqZ^ zp~3P`6vqde`KI)~Z_>TG zYMy)b%!XxGpUZVax>dJAjwsL#sgX6ZZr(~u)mt4+{`gvcEuft;zC70B;Zh*k+y!Kn z?}W^1|8muCxpY^h{jWXxfwaI)?38O7ImCF-{4nHvh8$CLZ6M;|j-)rK!dsNJuGp-{ zyCYS#8`1Odc%gybk$m>LV@iU!XBsRzOlRlUa@`>J(sxKI2`a_hBnBH6#@KafFZVd2 zVN?1_5@5$I*4vZCD5X1_(-$8r9SF$wuu+Aib&!grF#t$+7A0L7!e*Lic?lg~;NJHY z>6^E#Ok2{{uc_*`-V%)M-)m#rYuBw8XUz_&_?w#WJ%?o4r=HR#O_igrsgTVqYJ-Gt zi)wc{BGIZhLoq$GuBxdu)vFE09Zx#-X0?&{x;%wU?4G$!7ce zWeamzMOd=5&pvw;f1_s~S*#(p1Tx=kW<4`jzmUw7xBnqPJ~YH$)04%Sq6=;^1Md#%ivCU!tv7Dtp>y_VQ4`;6e;+MTYcp(fslsqG*h&1>Ap_+!tNr?! z430#XsoVs)j@4GJ*Q+6M_T$?|S*ke%&gew;#LLZx*wfa0+8^7mKbN)aKDs3Wso?)( z{2kRg-wOB2`ZTw>^N($)2kUoIZ=MRoCneVZoQx=~x9}z5GPKa~rc^nBURl-jE<9;* z`tC3JO%m9PD?lW2WC4B+5H;jV z2xr%LBk?cN5f+aIEuIJE^A%T)*YiuwVt{5;S!xk$C;2*0(4&GD5#cPA#ywWxjuT{X ztbpOD-t=DqdzdH$GtH`@f=eS(yT4w!V+@M6e%i{^qEv>K3QaOa65-r zzyOl7h_!|MACISoSPAq15Rt$Vi#-j@AY0Xc-4AKJ@&sK5egbBgK35-t&^K9Y-GC^- z3_sL~B>7!j;rj8`NDE}I_DWsd?z4VCg|8(_7(dikL@)O zHL=TL8?-C;m+B_DOZ7}BH5-WUX^`V>78Xao(q*wMHE*CXWD=_a&49MC|L3hRMGi z9f}$NU2+UxxS8*}d`th)AF-zj=bKnpn0lbm9R0dQ@e8oi<1H;l_CG31LtcL7b?KHP zwaTKR^pYYC(FLaG=&;6gqBX3daLUD2!HW z(mOan-BMen?9P|D%Rws$1338IwiBg9&SN_D100G~DtcTk*^2oa40)HS>^o)=YlKL=UoioO>Y#@XC368T}Zt3Ua zk>E?dH877YO6}L4QxZR0MtQ!D^!D{wZ+exnde$sC5(-2}_J0uBAS})SF1=0X zl%~U?<>|G31`LsSOa_|B*sEmsoZhc@EeE~G)=JUDSg9fU_bZI92N)L{2^6COJ3K|i zCQpvhmbV>X`3Y?CjdI0yM0S~pWXR#If{cjH<=dlSbc=SqZearNmfeEdK);|WCKftw zbbkSh4#OCY8mV7CD2?|mbZQMeqKnd-4DG}RuPwTizuHp2sn}9qq1o4)GMeM0L|C@W zWPf)F!D-<)Y!3r9fF}IQ`zRS$fKwF2JWu}%<}cLbaG5F^Tax}Yp#d3m&nGfR*Q$vD$T7=Ds`@`cVhl0ejnT zq7iV_OdKf7micx?U0=iH(l&Rc9vpY&om6AFEAREcWHj8B5Bfj)`I_ghd`91UNZ;d} z5*NX_QUvv~mC}4>L#=j+_)Ce}@3-Td(hhB38J+)1eZEQIH)F!Pg{CdwLP$Dl{6N=p}Wd&?kZ`n_8G3yEnelfc$MGcRl3Ei{1&%X zh#7V`!}*-B5?C8+E`MqBD(MAr!nzwQU*m<;gGPR_JkWbH^pODg=fq%ZUnj(+TUs;1 zss%z+c6Iujgg97zldJVZL16Oc_cfKI*5>ys_=|*Pz9vc0^e_XzE$sGX*%=@jGAsX= z4$@DH@PmC{(Ah=5NLy;qzOOD+a8s^=%{t|lbg%yQXY|ARdUN{M`deG$^uTw?4Z=8< z!r7^}na{KY!WI5%U-vBJD$!?H!(~}}yIb2AM1jg>3nUmRt!Mda;*R=TQB0L$zYe<;$ zN0yBcvWur`0oJLIc`XX2m=#1#HC4zkxtdc%M(0NVlAe4#t7ajIXvX0szBPm`e3W4y7BWuW#UGztWKXL*JuL5&1|UX^N)q-8+bzMTBdVYZfos4*1)n7_gWUbD=+U$ zbnvv4e}WwKCS<>WTv}8iMD$PeNLp`54K`KhfEvZX9=2BPY5||wW9uNxH zUMmFh2UQ`OKhsm^!3FZsV?EI+{<~8>Wg^WLNMydS&WtX*@mzj1e6mG-E2h1+sGlq##Xk_FSWhHM6w^9~dLFy7cs|V`qi8U` ze3ofPBjkWXa%$aMN^qBMl|k_@cj_)YzT5vw`(Jzbfz?I?+;3 zr+d$&M`T6_edqRzTMzB^{?r(!LL! zF7dd01><3Lk1ogeVByN!_92?dPNK4XXI|zKpBNI3s5DvFGTnA^EHbYu{v{`JgX~;d zDoon`xcn`Xol9HNk4QLaZs*boz500x`2=HrI(-W<>wrK$RH?a1=9cBdI55N>|z$xxap~Ib@~KMi#JJmvNQ;tH3&(o5^)=LNDi%SJtmQ6kuKEFWP{TrM_@iS6B4u zd-Mn~@~+YOOD^)-fCLI_ZJ@~fcXefst84G=ka|i5_0h^w_NXZK4wA@n+3r>Z z{pc$CCf5opWsmxiPAKrt-CU_3`>QdBH~vC4dNHlWqoWLNGc8#9;0e;hA&5l&;lP{l ztJY^!ir9dW>6Zx3oUlPhsRdGJ_EYh~QCjH)L6M0mbE^kHcwuGnjkUT0d88@s(_2Tn z?$VL2N=n@>rRqpmvtyqwxx&;%XAYCd+Jzxz0%t&2+tOC~pT9v>0XXpiHstIs^M_@f zzXrWeqgaDjb^JAm-wh?KNO}#Du+n3#z1ys@xhc^@G@jq?K{tutK4Ap;KlO%3M^Hxu!nfOt!zsJH)Yy{hc(w*C{|PuMKI1Cd=xE zgkY5masHD|$!Wyuxh%m=S_%HO+{?=9Ic-k%sE7EKx!Hg6piZqdQ`{S(m1`Cq6lPq3 zr?Y(25s@0xETaajz&5Qaa_gA|+)KZ7v@}l~Jm_k`#<|UkGuf0vr^G)zr&{kyJ9Lu|Qf#ZGg9|1Lr;<+f1 zv(?J~2qUA&MEMy4zH~3Gny|rfzgv<1u|6DXn6z$q`XmBTqV;w0)AWg+eVEGcv^*+a zO4TFwtcJelis+xC=e(>0LKNL478-X#TFx`}sdw`#>Gr1dcI;?`#^XNOa`Hbn$s5Fw zVeE->lGMz#DRq$H_x?)s?ICtT zo+$_Cm$Ivb$2B%F$hboPDvdXaqzzrb(c$KL5pwE6^a4EOSJyVH9(Bdcs5)n&vDz*( zckDK^{n4>Ka$MV)BrI+H+-XT6E$hWqq6oCr)Lz;n>|3S!Qv)NKy@|Dr>W+Idyf%gRG}b;-?^#HO?mW3#yIPGfE}uXva&&YQxNKh>n$O zF+=gIjmoYQ3+XGyj#s0BjZI#qjT0^AOG$1-*^Pw8By3l|PWt@lC9Sv{KmAQuBYW6Q1G<_ZQG-SnqiEM8jr8p#HMG4V9 zQT5KiBXp)XTRYhr)$A1ubUuG9TEDKk<){#1YL?8^&zG+*K^1~d#seFpz?38PITfV_ zgfOKYsc3jx=7QpKYjwUmR@_T5#YG44g`QHJFbjEMg%w*5k+57RG#>*ifGt*(U>fFn zyMbJ~SkrjmaB0yIDmBfU)UjSTAQRIK$|e0LlA4@&U)+kLO|?%a1)-}@-6~^ylh$R9 z1}65_m7okk)<*@=(<2G5=Zc!8i|W@L6IGSBFlHN@sT__;H3yP1V5!Mou$g3V3~b%g z@?32Y(1`-d5Ke<4F$~8soun!aL<(d8N=#pW+L?g_I0lI zIJIv}bAn@-DSBdd!`OaCl_c5t@6C0hA)4xiX7x59e>y{pmG>{?CsDL$5)FfgnM1>w zL(HAP3+jC-$>?0qjBobFoTc8UC|yo#@-%Q!B*RDK#h|>3Dx1~hc}Jy8&&-}F2MaE0 zK@M{y(l*|10J6U`sxBnTb(aWWO1h@nXxx#wW>N{8QZ5W4F~U||WaFZs>CTyCmd>n; zhBr35)*OuZljg0Qu))?^^%gt4iD1n}V|C@I#$}aNc(M}R+4yb|$5moJtW499f0>^SO>|Cn6;uH}rvvzWma#uHG1S?n>4f95KITbgcSot6Gobl$wwq z?D2KK8QZ0*_z0>IS8l*%_h3CBgsVpciNxQ1es>Wr( zBMEqbQM2(Eanf+i!j)C6pOfac9?@~_oFf@hW67V;OL9LJ4&#V;xr<&ftUyZ9B@Uve zkKa_EC$aIl)Bj`YRL&ucx#KtsurNC5-!K}{x+~*ttsL@O-g>m@HG{0`!)|gG$;Fpk z+!L)krR~^4eFs04=wO0(OYOw4a#oiSNcYKs{6$cLHWIz%Bl`QA#xwF+ciKcd@p^f5 z*_Ck_^_il?&5@29iMtuGDBget%s(2{(gtW0Ks#G(te#dH=wHhLCu|V&mL-SD`@g~0(3ur%p+saD3 zDSak%(BMBvKQ7MjW*l}QyVP)Sk4f8>Tt(BP^;gb`kI=dYmlFv+L7>C3(hHi+Y&@7W zdIMWV0vrbChp#`N{NmSVrxFP8uJ-q87YdBzPmR6-k$Hna*Pr-htbPxi%UHcpc0m=x zT(a+@Z=jnb+R-FV?ykSY+deJ!g)aXw*jIJip zMwhiN%p9YWgQ0T>)X_TGl@+~B3$Y+tt0yGq^>O^Pdo73Yw$AQrx>{f`E~|Q*;__YR zdwmAE7}=-&;04raQ2!OKsdgm9lwfAf$?w_fxR`q!P{WN!`pO+d(ZZ9ISRk`&YK_%p z%SsVIW9&Y>6R?jza>Ps&r%b#bxb86m;Ln&;w(uzbKw6qqoWxi?l~HIU8XmSD(NAfz zcFA@=*BE$^OMozruH8Ks#Ij*9Bz~8%{4yBGc83FhlD~8?w5d=UAp(JZilK@CH#w<+ zDkxZPj+EkBW^ucG(o06-+ro@gw6=c5AL`7#ySKf{ukBuNdo-L}`!w8U?tK|3)m(N? znHRh*8r}-vljO58(4W}PiEv*afAg}^=4IyqZqvb>I$a=Hj$f-q-whG3oEIYFZB>sD z@%dJRO}h{%(fTW98(nFk1L3lBWqjs3p2zAi=%z(}MPq$h|IS+P6LD&-`i z0k>(k#9qsm7UBMZX^f7TRGRt&XYxzJx`yi(>NB^E)lCZ+qVS~Bcm?=sYiVibFoea` z@kg2KR|`Ke@8|IJn~r;B-Lj^Z%sF&El`c>04p0;3Jd48*Q(U(B`MNsUU_c42M%N-* zkT0QgdmCWim36(m_^ES4TTs5G20pEcYiGl?m)FcUy5x;Lj{1_LuEm}Ef$n=Oyf=Pa zv_2PO>u=T3x`JgyQb3ylwRWJ^qt-p|(DEh1^Y4DSM2Xcx24>LXsw ziwX9nuDSl8NA|NmCbFYjhxxLKKtuNBS)7tKcbYf)EQZhz5)%h6a`IpGuIbsbw{eR# zZq{7F3J5Mf+79Y42~P8s z+``5X!C2SqnZe@qnB!Hady`I4O|v=H6&5o({}Yj5s{v6RN?|+kwzpG&oTTr6!_DlZ zor{I4@rICt3r)7zRX3Ad(SH3-r?=nUX8U} z%~OlRhL=$%{pfAFy2NKilnL5ShVMJ>G|kX|_i+rJE9z;Q4^tmrHw~Un@}X@j^Fk|?B4n4*mOUhYkRfE7gR3ZxwJr3;k(K3p-lRL72<}_*vUX!ilR&HmI4x^Cu6Id)N zufCgIQ*~Z$66JRA#8j=0kn!Ic5K}KXYNOpJ4W%pNtUiWNs#wT2wuM!3Vs0jjCCDAX zU-cmZFBW+xW;DH%xWV7n*(1%(a6lx=xe!r`k$4uKm&vlQpl!-@FZgBIp(77P(ujuP zIW-H?OGLy3bkL}FJ1in7xZkH&d>tDf%Gy?CM)gh3(LrM{R$w0oJ?Z)Gu(T|;O6#4# zE3zwuKoyQ|BdF;)JDj!E2I0F>)GM9Lk%vCx5OyOHhMXWuY+MP+&J3dN@xYGC7afxr zsJ%M$NrDg%qIdi8I0eZn8!0aK@#KT_l?gB8)>wjBcX76Xk$8fB7wzw*VNN(nV79U1 zKY214d#k#+6Oc`m3R@Pg<+2x=TZ_MBw(n~l6t+S)tKf##qx?j1wl5TJwg5>qu&24db)+h-FM|)+O+*Il!iKEet}@n? z&dWUjfbTBjHF>S5T%~noY|+Z5Y_a81K=ecEwTTT56urt%FF6N|UsROH7+^FJRO4ge z=VRW=QA`SsM~Fo>+OpM+pxZZN#*B?N(d}QFw-UQQfuu3j){d8Z>GIr|Kbze6YC=$Xfw=QwbdN44V6R(1m)#n zb6`8pCNq_;04FoYcI%!DGXp7NB%%SfqP{dNu$?{xo8XwB6961rU0D|m>{1)e2^;rr zi;mqOaAavo%WpJ8m?Dp;-IqQEekjDt={?FhD8N{=q#`PW`D!nar1Z=2ZaYry5Gd(= zvyyOcI{Nckp(OOedRnt&IFEA_m{=QlXF^u@X3$?kuZQ$6rh3?%Oxw$B!aY=q13a3H zHBRGSi-`(-O=C^tTT(ESLZ~}VQcc|_WT)z2t?30vLk}6CaVFnx^sW#1(K${Ur*8!B zvJ*XJ9N%BjavLiQyjGJC&#fT}xPVBETZu+CN+E}wx$Qko~Jba2zRXdS-6FN!Bn(Be{3~%&Q zJG&v~oqHfkK&RIsy((HSWL|DdCtfZ^L<U0%3?MIHd0RIxTu^bv#+f68J-T8 z2i(;(Y;vPptm00{4M+A_udvlHe?j$5?n)xI?8qgdMH6iA@nv*r$oR@WQ11duT;IRS zP;s`$>flvXbet>;aX)Am(pA303!@;9Cb2|n7R}9<0)Gk~5;fNRqcEoBT{u3fW`U0k zFvB_C26q74K!CM30l#rPUTG8idU~+w=0yQH-B}@`LUc}ciTL>GNESmBKb)88mS@qw z4^*{YXso$K$WEG68qhw)!?h(WKH-#<6iCf;^{^bOj8iDXO8u#v2nLxaR^XYaH+`S! zn@r!N-G0tlA+?dV($Ic@!CX7V_N1LjznLFf#inNf54`DnO#Lz+D$?VLc!H8BbcG!P zQ1a4&&(of~W#oSk%KzLZ!;XD64OMRe6VDU}6%rIx8R!w2Gs5x4n9atT+pDG`pK$R2 zOaok6rKZKcEWf>5qpm%3UbCyyD08MyR|C4b=1dme@#$k}F^+nvM_n*e_uTO+r%-Ok zN+jqY6{b*XfeIH$p*n$@QdFo!__ZsexSWc&xm zZHYWOWO4Dq;)DWLpx5z&QXZ3Hr7R&77!5x!BuHf#roi(wf(G6x&kkAQ*rF1>BQ74B zJ5@>+;yR`saMvxG@-j|&VLZ4#7x+C7%W`_4B$pTL(7vd8&qma%J-Jb1#6Gj(`uiOP z%6@jkb<|*?vN7Nn2q_sbf9P^!1{wd>VS8-*(6$O~>_9JbE6-ti`7g|~LvUE}0ODq1 zAr_I4M9;bjVrsyR9OW!Tr?I3drp)t>mv-8r0Wv zM=h0MaQ!x<#}|kkpE5HV-sN*vZu0Ju$s2X=$vqwvS@|x{qgp%l zXOZJA6k!K=_vsAQnx}nHp6=y2ueVa0c>&;j)?HcN&ef2Y7IT(&#C0c@m1LJz5*#Fg zxj_W8rKdgfe`e9#AKaPe%V(9sJzT{RR+SX;#zntSBQE21X3P=4<4)@l=piHI zJ{yCM7Jc*R+>ev<=beG(%29986z4cprkd3`qYL_^vMZ6YR(NYO!K=-L6x{|8lByC3 z0;CZi8Fgo;Mw@ades*K#i|$3K(ZEXbLz4DIQ=?h#c{Nf>{D>&lQj#u2ky0v?N}Jtt zMmM{hUyYBjBFdVfc4ThS>Zin8%V{^ebhts7<+`cb-jT6TY~{Q*C$JNR=392^<-?u8 z4$RqMt>45@=fH^*nm*5q6(?*k9v-L=1tZ@dmMU~(4$KW~K#3e?qCX0aSA6Z!?uUge`wN4k-xMBwS9xGGpQQ_7bo$xJcjOD5;|}!c<4+f1V7xjt6=1?hmo%T=9Yo zHg=WW(euHPa6Nb4uQf}bA`Q=tZ$2LkV)$CkI0Yt6^*c(0xQ0S@ZH=vK>@ z)*-gQtFG)nOYE({ro?VTEx%1lI1D)$<%V0a(>_tUMh&!D0oX%a19x}84~PBD4a1}RK8h+6I!0JKvm z>bghGD=OJebt)nq91E-?N=-6_H15&(`k7OaMW@}k?bNbZRL4Cj@BGH=A z!qaV~gaqoj@D9lk%5J0sn;HVp&VORMcWjEv&ul^x+L6B2&v(6h!*Lo)vBv&RoI!Q= zWp@)eCQpBR{SWg4DZA4e#ElWM#=g<){+y^)@kDmkJV-;Mk=?DQ<*KmrHTqSEJ3x|T zjwFIrT9O+k87SLRa%aMJRmxik7ao%z4>{<>vvIg}b9>R7CUcm6?5HDcOkEj@sa4W= zXY3nxmc#ttGM0xmqdZd6uM53jdN|d)>YWlN{FcqxB;FCj4=*cve_2$;O4NBDn7LH| zlWp~w!!uvPGL~C1Z%4|!XoMDAU;P z=$6R~M&|^!&+vjhNZQ+1n(7RJFw6h|-Ft%*c7Hnsa3Y?buqc|#6MDv;;Lljh1DUbh zRx)MH!19DYXX|CoewjIICthXF&a5C-LeE{+4^x|cv?Gz`l|9e4cG35d6$q`wUn=nQ za|TffD~n9#1#AM8F+$s-8NucYOU_p3U-EbptoJ%%N>Xnir(%qWX_3xNZ$nEB@PTD1dn+T8Z4 z>c*IbKzLrGUSpZ5w&(8yjg{hQ9WxG?sB(&=ne0_I>LMhjEvWC$h=Pt%Dwb5P6nVnU zCNDVESTmY@uLimG6%IRdCx3v+{}tWo054*j`~fhAvE)(+ro~-O@M#f9JZjM~tYYsK z#UqNtuA*=#m!tOgbyU!C`3FtvaA?WvqM^qZ-=0r+;fYvht2<9_|0%ebcTWhCv6`FO zp&$z_zyg6$*?!`#n$?>-b_1)3N%D+1GQ2Sf{2*W&Sr(P$Uy>r^4cK9rso&i3XEt0* z?8^i}wep06SWQ9^h&8o96moZdVfQaM!Zv^`CIaSZvhWhleB#1$spboFe+irhy@G4Z z00*pbL_@DzxztL0*kCL$g*hw#uF!qT?4rh%`Rch5z_$&Uu7XWSKeuNiasvJ-zn+MGmVW%AU?*HHy9!2HqYSQp`O^V~b{gnQ<>)hO@y9tKL|ug!BWP0w>r z!II&z#A`%*v@7?{U(UC8tes>eOtoqxtG{ z1l(0hoS!~_3ZJlckEyLYUdAA~Y$M>NJ1sl5IgE07=iW@I125gS-oU=r$0r!4v__p zAuq{#Q{IO9h8<3`u&5_lD5max=+ARF%-y@T6jd3$>+YHb6jXmIf0V?870CFJcTWFj5IpZxoOYc2xVdoF)26^U3OHW zG)V*H+;a|AA7MwHGnQPz=wxm@%@0=wER99pXeIQnllGUCG^x&;bkp|HG#=74Hh*Mz z?a)?E*Zm?{1IVc398kl=do;<3K!4q&qo;b6|AMh1O^PhvoNtRbUjtjwQs~uj63uxN zN-*W$DXe$sxP= zi6wYizpoJ|ANpHqBH9@WTH)v7ZzRsFYAh?^g!)a_Eif+FKF|2U#$tK?w(*1L^3Xiv zz2137T|;u7aiE(dTlH-iCsk4WD8-|GIl6;+*zzQ4H*$lUtvg9}pg;d3=>NE3vr+4J zN|Xw4ahy8 zqU3vUkyHitSREe<1IO8*^Z{uzUo{?mT0KYoiI)!Sco`uxrjY~tBk7jm@*Pe$d%>A3 zG=z!aVEd!-=w?ax3jXdBG(ElGiIF`m6<+0u;Wxc8*KW(C-YApv-{c6NBOaK`8%2A} zy?YuXo&S6vi=6SJq&@^xhNa^PnNtOkn{$Zk`8OEEQyhQb{k&$6$n1{SxBsDn+XI{^R%KNYQ)5Oq4G&PX(mgt1g#U$bcPB>)Wp300Au z$X?}2oGHy}`Er>Fz$ip&eM|NfdrM?`+Ui9Rl6ISuIpuu{*-hfG`T`Ibuh(w-oZYcc z#;lFgj^n$dZgao9Q#!ue_U@>W(j*iE<92ft#VthjF|{#Who@NDi7rNZ_u{8G4y<2{ zsyd9-GGob$w3Msz)??ANun$6Jq;o|(dPu z3?fFpf%uBzEv<`1-Zv8eahQm9<@HxIJ)649c3wvMR-!qzcP6%~hnc8jp}C`>mvu1wf= zf#Y6Z%X>rGxku@kNfJ2^FY5A)&aYFJ@?<7*(qG%*xrq=zMYFu^-6pl>*q-GYW|%7`TWe(Fk?GIvJZ>+kki|79sneQyu%lZR(V#=@`1BAF=z?5^mnsdQu^t$xm-|eUXM0r4W9e20w3WHPmpkzCvPDh)+o*iBXvb zLvL#xg*SFy{t5bEk|32Gtr-49dQ?c13V>60EZisEG(D@#j65Zb4MS@*hR#ZeJPget z()5=1e8A8n^9)U_d!S_!h{`14$84c1J|kW}gb{DxSb`!pbX?795*f9CX%c9ekN81E zNq|I%fxIv)!8&@DM~z35;9B{g#-si0+|ni%Yl|nm#wM9?GSYnm=PEYeIq(NK;$Xtk zMJLcba1<>UhzHQ@d%NxI6M~&!bqH4%I68=??5Lp}-7d9!5=U?O3^_V6grkkTXg|zR zf43(SmcKc+_E}s#ooDZudMf>$-)h#*Gj%&m-3e1OY9~{y40BFezXn%35me48Y1mw| zbDqETP-~4Lub;6!>~G?4OAWUtRD62=_R|xX_Bv!Imk)-*DW&3J(nOI9!?H z$>rMy!rQR0G#{Gb{%k6;t(es*CCBQTzf+k|q{ryISBe_S;7!F0E@EZ@b>_G`(LIdU z4oUq!pI=T{?V68e2e3>lcv&yeT5>c0b9KVsJe{N&JX+66Bd^cqOW1#)j_pYwGf$q@ zCiwS~x%B}}*hp-HfAai*J~P+XXBJh+iWJ}Nd(coZtgvj~8huLR8MDWs!CWU3L(ZmD z+EHzfeG*9Pc>+|28Hv%1Hfh^V$r6AB0FfH>zDuZp0F=Z{OqqF5+{6-jct}6rI{?l( zA0v9>i*K_{+F0F-B?BI>tM~my1`Wbrl%_Gt(Eg$%Ygz;E5E)Q?bRMR5e;_gC=CAJ~3728+mKV*xW zXt8WJ?EpQ(qqJ<`TQUPa8*03=s+bzv-ol44NC`&DA2J4DHUofV1yLRoqXDw82)B*P zZ|1djysF6rLgaXp&b3po;8x2`eR;j#{Veg4ySi| zqi(M`p=a;bv3qhfG?x583wf>BWO(NXcH0(f?0IX`Q+@|>mwnQ4zegD58^)4v5H>8U zn@n@`-7b-jsA-c>jS!cQ3Si>Lo0y5d4;XkyUA)#A`%}@7fWL?k=+O}Z!70*D;mMD% z#k{Z{{g36>W~}!Dzvcr^B%0-8SzjI=l9Ar~RiP*%?XH_8dpz4}(5=?&Aq#`Ny!K9X z_uM={0j+5LjK|-Rx`#kZsk#_8?6|Kx$#-f?61|M^D=PybLn|P6ZZYKATQDV zdOs+9x00L&A}GGcknA4Jam}C{tsqXF-cU03J>AihK0-KN~pn z1@zCw+WWmvPhucHO>9BbeO%PU>{7vy^1?OnK}=2?x<2W+nrR*NucA(4KmO*Nyoshw zO(*iGR=St`f|KdAOx~|$asy11m&dPYdHeD5_~)M>kEc@WXDW~X?g0qt&mrXTjMqPN zc^vzAc{~+mf32zhD@SF7fH>+W#l~(){~ksh=wgY(?#a=7;D`LoPlUfof_LC29F*Y3 zl5>$h1LtWj{7lxgFHrs<4M+UtGne9`=r#YJm*4g;{#N z;+;Fm{h=6uq6t}!-sbDil-A#h#pifOT{Rui^O#t*zP-kL$>A}Tpd3?F=Obb!7C@PO zC@xW;7i0vmi`lW8F$I7eI8H~Z?8K{--NX)nHhIa@*&OAB{^Q2_Gp80qF5)BeA(yCz zT+-EBWispL{4NHw^&f<~B-m!LWHZent*o;RHR z6uFflaVup+Rap*$!SI8VuR!ECt2BEkbDV6a{^E0TzeH^;u(F&w9J|+KTZ^dD}z9PU1 zT815Yv-NC^m5e-u?fE_6j|yJ&6pOsndTbt1*(JJ!poc!@@);p2I8dZ(KBBVaBZvyU z9g3)o(JCur5yhlLAI%0@E)la6ScN#HLF7a5y&J_h+7K=V`EL07Ahx0nPG9*A9c znMan2?bgg&;N0sr9?ZF&aBe&F!4f!=QiXF192*P2Q9w?znp+uA&AK_{M2^$Zc*&>X zSKe`WXsl!izvdB=KSHDb3;8wJa)t1#0CO^mn!5nOi!hQr6CaF~{B5jzex7wT)8^0= zImzQ7t{sA=3<4>6z6DSzLQ-H>cvjG1c(yy}11hrFPXZvby&6u@GYZBQI7PTuq_&pd zB8WvG*E8{CAE8E9&fD!MEyxGHBOnW*0W-CwnOFG*PuT%m<=5vy{r$^w=C^om)YCag zC5NY+G5b_x`SoPVLn#CdkDbCcmj5w0<==iLmE~iRSAvTF-wvnz^LuCdbZ zZpr~Fu(#IAPe&{F)tj5)(;_fs>mk4t-YoOkqA7dtEgGQzJv8O#gGGU+oYMF|jiyYZ zzE46^?&kSHXv(+^|A*0(YN5$zg{DN9OdpG;yiQI3t7wWz&}U$u22}KMPsNCeZvkQs zUfkfDVoIBhc}--=pYyQFzi=_c|1l_^m-6Tn*NN_^3u3Eq9q$Kxy0#dT*L8eq9zcvC$w&@vQbS@OS%v7GSJVzF*4!J5ww$Wefw zk!XJu4Puax|4x{GF%drt+5Z25Ku_QGHw*MH_6{N}hvyKMz6<huD#+Jx;JKu5uPI}`GnI~J)-P`&0ek>rg{M?a}pBK!R>prfD zI1orP7CB%fzDZ50xf6l=#pelrS3i`(d>=ig!;t+tgZRxC$)w|Hk|ai?d~!TZ^;c-d zC&klXUk(YU!P~lk*nni_8@YIbTs#f8sZ~2(ZS|Cd(+oT&z0qxsN<>XAy1>2o2~3(& znE`y zou^CEh4{MPws~1|wue1B*yittTJD~%e*TU;R5L1_$UsXt>OL@+O%hRe&EKW5$H74VE@>Xlu9&b@{1Ro(*`O=fn2`Cla-5Uj0;X-Ay-1%&5Fg ziC!rix+Akf(`1QMb*`__*TM4@dOCe~%J*tV+4`67$S2*;8D8m^`cAUn86%g@Y_Ak!Ll)N7P&9!p;iWS9KHwtrNI*5U&!ksLN3(!{%26@tDezy?Kg(54z}H&y zQ5cKVY^DVS7eEU;FL-+O*FwI>jHcTtTT=K%*As2EdHz;c1;s!iW(a!*hPWU6cqM#3eswyKo0B6F{jLPEtFv<42f$tX~q~< zu?8MUY+EbF6+#GS+$AW(*eQSgw2<rNvKXN6iM8R zw(le|%LsH*Vgosr+GSocg+>>ILEz2p-a<_S>Xs|Hs=lik3c;CUDmF(1K?~^V_IO1r zJ(SHRQ)4=G(uDGO_{+Nl5>0tL0Z=$q=H12OYBi%fVMde<%WH_l8h%xhl?9=<%fq#1 zP=%M1OnXb~^PY7R#-CG)RtTCv5OWNYsR9kfc5pgZIMWwjNL`%C&E>^CuRCw9y#3M5e6pgsX&&1=OEps!M{G(m8g}tCk4W*})c~ zIsgy>v9=?tNI)a#*?GeAz)|z?w4^;_IX%3REHMvUF1!uobS^jUK2=Y*SuKD;O<%hS zI5*IuCm&}EzUr_(@}mf4=&jn_sg)SNK^9Ct;@PDls}!ZH87srsPv)Aw=W5Te?~!T- z&u(pBl43UPOHz!meTncv`L8FRL3HMNEU)<-*r~Dl;UU5n5sB+L4|o=}7!L-C8cmR` za8V0nLmO5}^Qq!<7yEi~Y3%MlIm5$-kj8$0iO3a7oSy~=}MePXNvzCX9 zO14p$URotP2s|U*E(AsweEw`%fEzlPsO;oGg`f-ByrPNsQPWi5k5wV{XRCad@!&+_ zPq%7e#%Qji2qy7INy`XgTV$BqelH6TEriCgAo_uGhhy_{B%q0!cz=$s}48icRuBQO42-DP5~+k=W={L8(m4o-=Vm;26S| z?&1!-7di?70&NY&F7=S{A_7flN^GV25l=kv!0)7S5ULI{qU**XyD`In;R^pH*n{mG zaJsr_|3jX?YLQ7M{g5QgVHCO5Yl&8$Tx^Z0Ut^7F3+?Q7utc$21ydFF$l4cz&49e> zNSsbLSz~6cfnBd7eKXzJDN7!biyk&(HJOscslFAGo$8QZH)OeNq)==C>FL2`3s%9J zB;pKe9Vyf*V>6Ga1}JtxF>;8v_zyIUiv>7R~@QvwOlbq)!$3r zlz=Ay`Bn%0UKvBrCg&V-4th4QXtxJ^i-ofaOwX$QOu*J>W^59Xz)<$hLf8R&oe;Iq zx4l?s8Pe`CNQl_Oc4oZh5G(y(h2=S^!UGTzeW^vA-RS6k~mP zl)@H|{C$oh%FSpI_n->qh9znJ)q>TJ2h@VHu%RQ+L%t`BWw+>>L%uqMKI~}?BiOs~ z8Z+jh_CxGu^6`*oXX4ktg)$`yebf9;V}F-+TdJ3l8E2O7Om4B8w&}=k=Z?K%n*?ZE zN!>pZxn5OU6eJ4W8yWN5#b3S~#T=GQ>^Y+-%8Fx#f`38gtOwFO@`Z-RwA(A9@~G*1 zz4qMnSA%F3?&q8j^c>F&;o?#!_*{QfhQ(B`OL^=#bF18;FQwX{WJiePz>WFE>G8>oPv0f+=@XN7)6){3PC7?p+0V5(S1&I=bc)6tJS8@|ji|8fPtsHN ztd-#4;jbbH3Ad-~kgKc0!0d$*0_xkuV_wv!Do~vxNLg=pB^7LU{tQu_LAa-(ho-Giy%^+Gmn3fN=Qv z`9yFRn(dRcjzsd#<~59ZPre-el_Thhl^{@>BMCs4-pv(~-Q^at0_2V|nwRM(WC57M zEF*kw1CNDb({hkQ;QmiUJQ7lu`2BraqVfp;XK5*O8GQuRO-*H+-DiYdN5+t^g$=0{*Bu7rUG+WS}=#G|}wNlF~dIiww6PFbW>z0dsuG%X+P+ug3 z{=ucITqdutN^$z?U71&<05mCCsQEX#eQdoayu&Oxtz=M3q=2xaIEl&-yt)s~3j$V#L6&UfW8VJLcnV;g`Kobp9p zkkv%=RK2Se4@7+Xjm0yuZ1+@E?4VU?WfZ)V`PN&c-pk&e2>G5|@F{Zh=rj;v&z6fZ zJ&By+TVFFI$PPDZpTrJg+KI-|KcAfcqAp*e+hVthB9i#UgHj(?@4mKJ_P5gZckEY4 zf#1>HsD329)91HpbsJUMek_$x%HDY*N` z5M)5aPG~=5G&F}ojaBI zY7G{Lr;Zy860auzS>LTlQW%|m@0?$Zyv))I{rL^SJ|zorDo zOzp|yZE<5I9S4UF{aO4K_8AS^-|%){?3HQp6>qHCyV6qWM zSIrp#`xlNJ;QB1s(g2ZCvlV$pLQMVLV}|io7>`9qO%dXN_Ep>}uelzJ4iy}Sa)Oe* zC<`_lR?X7pFiWB9VLsk>j-xL(_tOSBB%V0L67i+GTNK%4PA0diPhTmSiHtCN2)i7poe1WdMq{`$zw4h?Xkd2lRMa5@pQ^vp>S6SNnn|L z_9G&;1rghXG)N^O66iQBfux(7KLh&@BtGJ$#43qu0^iNpo58?pDi-_F`+f|99*)|=ggyW0ADWOd@w`V#f*e*mg6S7}P6tpp zeC<$9iMsc6N}O{jr^NUHPKg?v66ZKh32ul}f@B%><77TYY|UR_U#2@HWrk~ZG{<(l z25M=_Az`cz;+R+^3AoBhcxJMw5fz~gfTGjK^6nVTEP|sE*8G504O2deA$lgcJ_+qg zT~MupSXFj7Y)x&`CLe~gOa)izIrYn53k5L99##qo+*Y4;$$E+A`DD2WG$b zlf#rpfl_4CBwk(h|{oc&3_X#4_&D1Q3edc`rSQ8adA~ ziDMf_&%}Ws_VAIeJ>35MVa(wKe9260ezty?)^Y3a?ZF0Ef3IX+i@pP#8|;^`UKYz) z#q2@h+hi<0je!gK_8NTn2&tsM|Md(K4g zZ9wK|b;1|nM9vM8^1Ftq%u8d*bCK!+{mq}!ZMqBG1xGu6Lyd7K&En&@fYG#X6*q9d zd=*qDlFsZv!1L??^HN^}Wu?qZt%J-WL{z^J<*p_JH{qAM0c)TL#jo8u3cG( z=^^EO$Lmf#YnSPMW$7w8(Pfo-l?egSkcCB7p495H_%|dmc1;QCbwisH-oVkIO$kpN zWJ)0PgiTp%hLD68lWnFc;R!J%P(Gq=%9L>BFQg7wbu)|znfB#=d%`ard%}L~39{u@ z+Md9`LFcY9V$nRy^LkofN?R2^>sS@aAPUi6KYO&S3K!3pZ>Mn8w=4C*NSoXOCFkabSw>H`A&Zi9nUW688#Oz4RF5cpG~F#@{WV4-_kJQJuMCY z1p~v(x+5DHv^wVW7IkjV zD6uirA4tg|mV-~#ap)h)#E`>hj$~r^l2jvh?$`MKAxsQ^`~FcjG3-BLK>Cw4ROV=z z82X^6!fa`M>Z*Jcn!X8f5jVpL;~dfH6IZf;ih|K!QPfw@JoLCp`-eBsadl z=6J!Bw(CUstbnkNZHa1b10{V1cjf>nDF*TJZiKy{P8Z+*Bv|6-#>z|e^9E-z`7BvX zCJ=Tb;SAcQk;-C1!lYE#jmQk}qocPxoHb8Are`-_*+kie0MZVL=WhRRo13HG7`Pzd zSx80HFTq82l$L{2_=?*jnZ1!-2(*cLz-oZ=RF>@bUI<*g#eX6G{W^C!XfrD|9WH-~ zJAE>GbsWAQOI=<9iHF8Kov6J_dvj8Kek^x2mT@pJJJ^e*XJ+D@pF2}&?9T>Fr3+Tb zse{V?6vysoH7a|<8E}#y`942+>wPknBE9P*Y*{>>{qm?-2%UUDckpt}MPfrrA%y@bXm&DWG)pM4Pp-<;9uM8|TeXrxzl;8fOivS-~b2)!j| z@hKN-eV5xac*9&=we~~1*lP!LJ$}w|*IrrmpT}1_K+b(QUva$lh2JN>I%Tg+;j6`} zB>xS*CD6p>$mLR~&qG)c}0;^uyq*vBwR>SI@{t zbsr3`23I}CfmmyJwUq&{E`ERT>OZrtV_&H+MCv#?@aoyxTDd6y@aSr6sG0W54}h+w zSL&@pp{vt)i~-ilf%t0k)_*U)T1W!7@h67UqbfLC+~)($l$rSIAV#)3I(Y*QqK(p` z_G^4~vs^t4#JahQdx^V{GaQt46{mwx*4l?XSEH==3xwK1SufNm>!;*G3SfP`oV+K% zI)FNz39ufBuf|$95MRAe|hCLHhw22MnO`~Mv)DvzdFI` zWMV#vjA7;27K48JWIPN{0!>J&(05_|$Ayi#vFSR@1Y0yVI(U4fEAhM%?3Vt7J*$+F zSUHRfe9W;;V(X-f(VUIw<0GWsujvx({Xq1z-$74nQ_SQNwsYaWi=B>)dCGxLW2l|6 zau9s_mDOUZjEJf7J3l|9snUT@TmMG*v~?Iu<$J@Y--o4A;L`_LDkIub8UEMd)7Tk- zPe%YNW}>H8(ZTnAF!XdaMkWGBGSJft9lF?-*!Nw%`sE9FdO1iRz&V&JiK+dU!PC}I z4fRQbr^j(WTOxgTT`kkdN>GXc6%AU$jjE0>kcp3XK~Kp4R&^ zr0h|GpT=HyFn+q0Qm_$c;HRf8k%fhvvkrcGgN|JQ_4OXjAaH0gxWQu08`1#k-5{l}dfx!*$ZQRXK8pBh zfa7WW^h&XN9~nRG|JU%-e%)wrkS%HW^p~~z6yeUS2FXt_TeA@X$jx=}(?i7rD){LG zc=^QS4}SWSIWpx3p9uhz;J3VTz+~OZ`6jmP^Kz%8-?a~11)cpF;`00kj;inO> zUHtSKgfNxHPa^^ygrAP_geKmw3buqD__U)>)8}0S@Y7#g3}HI>>3$@$;HMY95Bzkn z< zpZ(lmbB@8YN9Y=47iFzaCabZ|D(ZatA-%d35p9LBGhCmc~y% zD@L&Dpq{UX!%yqpv9HZP6n+|)D!A7};in}iYry&eVaz_70P2GG2cX8!-~y-#Kxc$x zDM$gR@8U`(fVxFLp;v$-BB;N4lNPH|2_31EGou0cG_Yy0ydgP%H0%Ljq$6hD6FjYP(#!wb;OV)8!P6KU27{;P_JgP4 z6U)%bbcB;UB~9B(I2q_Xud(0&3Ko&hWxBi(pL1^dtLB!7S8N< zPotFbfCf+B@b3UmBSEFX(_g(=>f=R%F}rq*3+@zaN~XETAcbJHc_|2%$r z*JT;K|KWU_vCN{*HTn+xoAA@8^PM9(H=Cs%v2$;E>%gI$n?qT7{)71Gz8O^JXgN1e zW~v<4x%oKF{xJCIaf9&FXG%w9;HUr8kit)Q9m31WUS@~HPqRqqTsHmq>8hI1f}cJ? zyd&B{q3wLK4b)=w57@b2r}r76B7DW`h*?fbn3P*$fPV!;U(ou`;)^O^uxF#~;)Q>8i6$Eu9EU0w=)nhcEnw1KC zwS%372M$w_by-&;*8ChVU;F+i@Y{Q00x`E^%9e649u2|xw z_)?c zmm`g#o_>!wCjPLTabXVajUCcAkqe9(-$?i0c`B&x8~~%_Js3reYSyF!q@taSI}Js_YS8|d!RcKl2i1Ebshd|w?GMiI(tH=!9X$1D(9=gL51G+AG?SO!q?>+>^`pW|`*7SZ^3SK?sMj;MJz_W{0umRl~t&P$7 z5a{YVUq$Z17opMB0Z+ZgRg159Hbh$m%USP8gz z56FI_=_xKa_-bbfJqTh==70eZ>rvm3UOgCMeQE~8y5yTYQ+?DS)_#Fl7er#0k?LW_ zO2JsukQB!Hd@$BB%z8IOfYwzc7m|!5i~m6bLTqTi_^%w`U)lMxEPoEt+QcNDMp~Dt z4lLJ+if<>Aq`YS*$lkE;1yJKJ=(kt$b{aDsR*P2R&;5>znYLozkzlj1=W)SI3wCUk|`XKYaA?(S?%DAl*ZImkhEbY!e&8c?aR6KY%6SzhFQZ zKl#W8gbaLijrK+A3mN$6$e2Gn@aVI|Q0?^2LGb8+{l_Z!Z#ecLw(YuQQo4Lt%w$5N zjR$X*byb3q54mHV+~l!?Bcvy07BynyKUi90^VzjaCro8Y%Fl?$mOZ<`O5gk*dv+}z zuxHot@h*(UE--A^>&JOtO`@dv9rDG*u^ed1ZO*&cqPuFP8#hSkdVl2iASyz z^>|z27xl?x#@<^8fI4?LdvAqR-@&_YCHR`;EULL${Xx2%1TJjA1vrI0xby~GNp>Xf za5vy8@8wbU-y*GH#{OF$?caW@0E$1P#Ex6UchJ7YJ?LWou zn<@wL`wXD&H;x|s`Bc8*@-rmT!W0->HuO1|&x3sa0DRuf-}+x*_aXCruzPn(Wpr!t zApG;!pzMCk`98chkMf@=e#MfC&U>ls{eGEw`I?yo3M2yPf>t>Qxp^?fwVjgAj38xH z27g3Z-jGUIU|QKK`*o|`oJr;_)V#Mjoc$}7?oAS@zp1HoIR%lu>+uE05`5dZLCTex zbk!GTUD2k7JNp8fs?)TM376&?m6wvL>GE8}M$^|y$|lvy&fNCUz=VnUdixOaIRYY> zm>;w!=c|dMOndU!2^SR_l@}G7_SC|)e!ex~A}{A&&dV6`MAjm>lfave<_7Mni;A^f z9Yz|Ou(X3FEjpcD%6V`pf`4;s-Gx?r>t5vX!F3I(TP2^{Ith>t*-ItMo4H0j<`x;k z?4^?7EfKgv__hWxgnfHzF#8aSOY|!IJIOPW^9?cI8)~2Im3Y$Vjrk}vKP|nNKB*%$ z#dZ+-i3VaTdWij8;s+U7+1_-E>}o}Ql4#E@I>3F-a?uXr3(8I9C4i)^Sg~{JNxlxF zGT!(C4|?KVCk?09*nKxd9$*X+HDsfk;nm2?`%0_$T15R{7_}n$`#h-X?9I#o0tQ)L z0%}F+D(Wr&Pl#($}u}tzLZ`6 z8@DWDWH#5lVvlMr;Hx1gn>E%iV)0(cqa%t@cb9!ez&>U=i3?;lXjk1zyD9yQBc^zf z97s?+PM%GCpKGDCz}b8Qv?AN&iYG7RhH^PO=0;+LliB*JF|}hZTWDThrM8g##x#E0 zq2lG+L+qQ&Sg<#4puCm2QPp#bTQQ#Cf?G4QTtDJzofEWmY4Xq^YL?Blev#W)T*tsZ zLPsz~|IcW?S$e%Jh`fiV9{6#@8x~vNiqg9v3MPqhhQ2;>S*eVO1t%y;Ji-KvsL8N_ zzKCZd+i&k+_8Uv5iukUUxodE zl30*}+ne+hk{J_dIl*Fcuh?Z&h-Afy{Fcetu}R66$vJwWbW=fTJG!<9lZUZn7Z)JS z`n*j;yLygg60T|B%Gy@`F@o`yp`>~6g0@TMpWqbcAwMRi|R=`3zjp|E&sS58ic znKLz4AA983IJ>oFYF>*#8>EIIvQx?@*?^eDrIHq(k1zcMR3HPklA*TL1A5@-^^n&n z%OPU|BuQA)zq<8jJ%s+MWEZ`iLnc5yRo{X#bN*5PxB1z zLa_F*udH!L>w#7pqng}rc}bv`$e)T_of?y{dQI0ftoDY>UkgPydUS`)qECf+XoivE zAUYqK!y&xcQr{H!80m*!z#~Rl@C$gvNO*@A9x)Q$;e|(xgm-v_N5DJ0A^3&x4lB(s zbXms8EUtOGHzS@1qik_#hP+8`5B2xhiqbXo97fPHEl;1?XqIo#t7ESb&zUF}nMJKU zAm`Hb4vxd>@6qvD^`}~D8WW9W)$+Ym`%z2Xr#l9It@p!XL3)FR_xL%W8p)pXWZ|eQ zG52orumUj%ceG-6B!})M!4Vys<&iv$JwrpWTl8_P@D{6_{r<>+?r{IGa@L%|OPq)T z`adwMUwkS%t1WeoPb(6eHr#p(W8`j9Y?Ft!QsoxZk;JhROZu%=Y~0Mp-pzRK|Kzzx9~I*_i>*|0tW!x+Ur^8J z^B`27N!51jz-pc%)dWU`K8KhXD9FyN@hg;u`ejwbc{}3kxM6|Ro)KY|<(@=Nf@8;% zuafNM0nd7==S)=T3m1HXHXUFYrBNncx{PK;U&@ho?WS0PwdU)_jY-*Cr1iA#Kt}uA z7yhoFc3PPl4QcP!OG_?C{tDxcAcOUS(<4YO*zDFyIK>)h1pTGsjbEGXX0D8ngBF;;Ik+nY99 zS-QL|)4qFim}3%X8LNk|a`I-U<@@WR`bg|^;ZSUCn8N%0vDl+>k119^q%Po94fHv64Z$x#95DY(8drbUw#^QHnrHfu)%;6pmWuEh#WzWsG?1hQ2 zJ-aKQ$T2i`7n^Q`?Ri9wER>wGUNinTK0)SAoyTfoY-~B)(>JMjzDHeG9ExsEs*PG? z!x<|CUKLXNNkeNy8|Vnoe%JC5fWRA>^Giap=9#}HrCTi5k7sB}ICg(oehI7ik4pKG zc)dwXkIhy&2{X5)-n+h#opRsryj90cK z^t+=QQ2X)dtNpP1o6OkMq&fUid)boF^ftV{+?S|D+X<+Dn(OgbiE74_2`WtG1Y^!$aQQuU=scB(L8`Q?4lhxMx$&cDWL zCi+^|1Yz;g#&GNwp@g6^`y###JvN3r(=}Cl) z;xC+}psQ${JtrpR58ndQZ3z3GuK7dH<$R>>^8ODjaXw)2fk%zSTPYa5gi^?o8*MGr zpJ;r6t4a4mFG!j?zm%#9Z_x#}NWp*9-OCiAk#+OT@>kW9B;Tu@BVI5Pu{!kqwJmRk zzqbBOP3PtNa8>7qW7E)0bC(Rjdn;nD`$qL*lX1?V`NWmFi%7p_RN z5UZbmm@};C%i~x!BZ#Cbgf<47_?WN>FuRsL6BYMFSVga8&r85Ax?mT(B8s1Row3Rs z_5q0y>S~`~8ZyERIMNWeFjo;#6I2IGJaH^Ex$FjF8kJj2A8M~L@gi?PZD(z}gk45t zZ00PN)!{-}WK`i@uJ79S=Npb8LsTwK@J002upSSYuECOK+n?`)d7g-sz(!8l&GsmT z5#PR=hjn_|NGw{KF9S@H+iMSmRG)MWE5-Lq_zv5UNbKTA{8?FttD))TsPWG2DYN*>}cFVpLv87OnZfrda_AJJNKYa0v5@&cloit^HKp5dnJKjCU1d7{4Si zyftVy=^^VmA+sw?yYgMrE+9b?6fGq;>HgAFE83Qu7AKuE6EYLwyI5As61_^w3JFyBwd}et(rwOs zpCa@9hH0iMAlrpmPP9WDax7jaoQ2(Y0>;EvO=_q~abYk4-+E}YlTcKqYY6sYs=_Eu zDipeO*g(YhmW*&+X}(N0B;xZ+t>}hv=B)I@az!9nuDq_%DqNZ`Hmm9846$3uWD|vM zCAA=7qv)R;{Yhv_=B!>o%D2n@K82;>SZP5>RprlFK1mhcY&sKz8yj@w#m1U$u=k~! zz9Q;O((aL6{rs^P#YT?}qd%zw8Tg%xRqmq|t)$)#erCLE@Ut__LgZUt%WPa7_GS~q zM3LhQ>U!96VE*tB8xg5pav#!;hkcLLJQ8Vsw(b;JOXs;YHa%MrA}!_5A6_T|FZsCW zw+Q=;ZGr|SCDGVwN6LcIK+8fCh8WSA(7lY)LfvIp*vwhxl~~!N?*`fTQhn9COZoY?MM z*W;dS584+qfj%vr7bcZnbR!wErN4-~dk67W^RzImeI@OXVn?bD*|#;L7L}^$Mfteg zEvig5z1aU%W8EvG?XfK~gri-3Xo?CvL5ausO*!(^#xsBa% zYQ7Or$3%SFYQADMVpPe=nm>ZQ9?%iXw^+Wn7fh3O;%2H;g)Q;!lcM|b7Mv`okZ}N& z%4DB&%))SiClgCq4tvIEot8_8EKeG%H$&hX5Le20n1{9|*1riAB7`(wFXY&`K?p3v zapEqKD%)q~E;>#dV$p}$dO*%S)?MVB>qxde>fA3#W?NsDCvVUO)C?w|=f5aH$j|Vo z%-Qb`sOs_I*y(HeenegGg_{3Jt0Bqt6ROYGQ}1=7vikh!-w(thIbq-11Z{2$i+a2j z%@M_Swp9G&y^Yv4B!OgW*a&u+Nz&wXTIBR=DS@Qmx34eyw52YEb|zY~B||KCRk-}| zkY^8&0rW{{cyk#~(Q9N^GW*gJtqK1flDpl4i?>;-QFNeo{jX2}1~Du$B_a>b(^A4u zs5ThOeEf!JpvL0cIhS$d%NO~>F(4}3rGY1iHaJBU?nyMU8#Z!9os2qCi@eHHyj=77 zo?N*RY<{I4`@%#&kM|;b`pn|_$7+#N5Aq3#9Z2Fm=mZyGY`{pz7qgtfK0!XB_L(Vz@xFSBnOd14bnq;Lh1nC1-11J>(`=V zU0phtTSn9WQb+S){>cvc-V7U+m<#rbeDV-O%hnt(hK$L7W-PAYFD9B&!oxSQW6DT3 z$y$MchB?V9K!$|^j2VAI=&P>HPOSL#ls z$?7lZ{REAKAD_7uyD8ri6X&a#_!4JwrJ}Xxx1!IoJU=5gLvx)d@YwEz4_KfaF*_8> zX>L}{Xd2ff#p5_YK->8O2%)SJp7wRlf5mZjn>)M6!w`x!W_ek5jLC5eBdhG)_D;_H zk-_qv7LFWtX_MXAJnP$PN60xzapk;xeg6dRpTFX5V$FtkfKMwB#`%CxH==KE%$acdEW>JmCHR4g*7KXEGkMgpH9xYh02eA#66Vq}L}zcF%R z8HUS?##{E+$|7hLH6b-5>|5wvP(9(>-uY8ZEvuMog*|BXA>DqNA>ml=er_WikWLfpbBFCWkC(7i@b?P;KPsi=Ox^`#uvG=YGYC21sxytrt>zjsevpB-gZxMC zB|8Z1QA|9+@;;f|lRPYIN4d(OIyLRvi^4+%b%`RTR4jWuWor0ZuM@jZ4 zsyoT@ZRf)*S-aWCP4<`R;=X=RP}`bnP)men=q!Sg?o`>SQjokT`eN{2acG$kjkG5@$oLpv4_EN2> z;}f!fyA@k1JYG-wH2t*u86yk^_V{*xu5+}I3b50mLI6Ql>Dy3$sK$x#4v}{lrka_7MxT+vSPC{$i|&F7W#9{bq;l>9zMq?qlwgo)D2W2Tv{a!_e+~mwB%?&!$k`4i%Z3y*amKfCeBTd zmaLxx=5v6heH@l3zZ%_$r2z%1%`2$M?MeucGQkE z*{v&eweA%zEf^>HvS&y^W^!{-1)?}K3!|o5Z#H&8#aZa76le}_wat8 zg<^T7|9R(O-qH`6(Ri}{ufiH(*}{QDwajRX44kwF!Do;v=a~IO$&uxnl_0F{F3smK zEQx_ce;Y-91GbNEc1IJy!YS+!lesLYeB4JDyiVZ;=jUlle#5 zs~?n-QtNYAol|DTMxRlEi2Gzj?U${62qmkn!2gCvd2|R#Rz1n+7LFWWP+!C-GJUt! zlBT#-bkS&EZp{nnT9$9BD+?V#6iod*x}5$+iR0H;@_X1u7-A7J zH)CIw9BgLnhSF@L)o|>4Lb<@Q|0!B`*t0dFwl8O#Jeapi$nV!yB5^4vCIf9GyTX?8 zG}F->-kCP3p@M3Q8uo*rHY1U_dOdar?Dt|)+gS5^1_!x*8A@Gy;v(K@Zus<{li#1 zOP<3SpG2k|>FT!lwD%ZGC+ZS-+*o~IR1A4Rwck{a1 z!6bLEUEqUM7RACH_peLZ?i)&(FAr<-LvNO~xg_x!B(GH4x*uafWd8>}{Cao!Q%q>t zY~ENu3R~Is1Stn(f9j!H|Lw_Pw38cyOk{S6qkRieKAJy{dWD%ODE^+?AdBQ<06slN z^J1!lKZhP*bfObgE}L98%H%Ot-}Z5STuyCkekUWF>L{_c*f%&<7N}*X9$iyC3Aqe^ zI)hqvrl`fVZ^UMDyx2^z^I|fow|j0(=^M`XwLC>bPm~Jak(xmKN3oERTZ^OpeYe^A zvzea@NaJw8ChN+im+FU0zt~MDN>!w(U@ZJ*{O`qq;jabk`&?3f+=!my(e$M!mf8dZ zWN*c|wqI*|X^NaNmkKqftfen>|DD0ftjiI)AhX+x=x#lyGP}Hhi8-*Fe-`#VUi(oi zrlek>@;zA4HmhtaV}4mtREC#BYESnS)C6uJET#dRZ|4Xo^n z^ms8X9(D&zfMc4K&1BRr!7ZYvS4eFLm6M$9O7u|n<(xPFVwU_HCt}}^X$!dcv1Equ z&2cVw=CDlaP@?0N64NkA=$%+3z5|pkB{5v&U8AZ!bA#nCQs6>w(AQO;Zzkj2Kcq1E z#AiRiCtd}vH|P^Od=ETIP;Jnmt=iC6Y2WaU=58OUOa|UL)CAC_^qgAR&TS?g(7sLf zXYhdFK>n;u1kHX#)tehpFNV}UZAknf3y@TK!Uw3bk1F?fhDp|inK7M%Jm$A!h^$KN>RA1mJQMd-=^BL;GG*?GPmF|<*klK;BXY4;$$f{ zCuM0FjD0niPDLK=-mUr5eeRM~!;-Z(h&scPwLcuY%~`T?UvZYKwutH&s9&>wwT1kB(aS+Ruqx$HaHCSG|Cs34`YzwM)!XBi;*D%cB8knW8 zZs}jI4CI-uho7U;lQD4Fm~GI<&Ni( zsaq`O37HvqIfQwos9Xkl>1krW{%%6aHh@2fv$DMuay7q9Yl(iaPJp{UCkQl4y`6zT`qeBE%l9yNz3J;fio3XWOd<0Wwd3!@3l$3ZAty6yHa~X-je|62x*eKHO0Z^>@&x z8syQIE3>^xkX*ZO`jhClUMhK&oZdL$0~WL?KtWOtF(;{VQez>%srC!FjrOO(ZH!S0 z+;-N$KERl=xsDub&ien}_%>r%2Yn%Sgo)S$j?>*#@hlBJu5x_tNMl8t=Na8&Kj5i3 z%iaE(>^AcNokf?0;(hej3m!nWvTxC1_Gi`|=5!W=-cs3eD;FTR6JK3Lrv9PPxsI{Wi7-b?_H+O|vr|-V z;lkUM!qZ*RCT<&t{KK&;*rsD3f)0IO5Oi91*Nj>qbg=h0m=+*#1Cez1X(Sy>;t-&5 zl1eeAc@O*@75JNBvGA4PFdcRLMD=i7!{&_Dtqzi|Q^Sh;GAquW*vVwwNv8dC0H2 zl69CZcbZW=H%u1@=61`o#ocvI*bQhqT@mg?D>vm1%jv;$(dInwR6OL_I-pL>Kbi#{ zrR}$#EtwCPb~TVay7-WIn@dsc1+v#bJ0hwVsGewI4)=Z@9Ram6E@Hof*IUgY8|Ia^ z^0WZTas#% z><%xRq48i<3aXt5sF=!xSxWez^HpGYB(oM&$#4La9|3^}N=)tU?xBS#^qxfJMfV+F`|$y< zAN(er*Zq6mqXTsZVKNJIyoyU9^=Jyi2Bc8LePDbukPK73eK3sAYJ5e^9RTv=hZL#6 z_{M19pEEwZL2GcG41n?R)&Lk^tBB3O_|7Q{Fw^}NJJhBCW;7Y#%;69e9};4FA(o_m z6yHzv;{JecL2B6X9KI4z#E|CRq;Y&(ft@zFK)y0xZ|yOl1YeE6GNkzxya1krh{7T? zkaIlM0n!)rfG9x1L`5)@4oNI|rN27@<*x=k8!K9yUrE*0{3Fk1NtqkN<$XreO;Tv= zfl|?>gX#-?Vc*@lp2p&Ta>rC`8oo7zq5uta%Icbd-7^92$&3;M8h}L>YCjrq6YH;H z{k3y%gB#nLeuN26YMFkbmrU?A-%E{^*q&riA$R->wdMZ=-{RX=`&F6aK<}0ae1}6s z##}$^c9ZqBI551;Z-!%4xgg8Kv6=Y;<$IM=Y3CMbDGdBw;W*46*~2$g+TmjbephJ+ zPI^;&DV|P!S&wCEL~y#N5(<7@JfSzpmUv_i91OZ^kbe1sc_kiU1=haGtFdO19@20$f#19(BcX@fJdw;9nx;_pnh9*RE8 zT?p8pLe^>c-6k#qznc;pEAYFB+9U1Hd`j?l7J}v{{%!EP+akU_wdeQ4?=U32jsZPG zzIZSEUAkABU(_551A~lov5@^ZS?k6N_|9k&X^z1bDpOZsf&cRj^~%7cSl$NWQ@B7t zZ4LN3!QOQaz}|)8Z|A@tU<{#E*jWm!mqOh7oGjnzl zd(|>M+pB}bQsBPHhXD7Li*JRlUPv#GJIK9pczoZ?d^fm7P<;;2PooOJ^mT9x6QV%< zS~+A@5rm&yO(XoC6?h)@@s)DX(`Ch`z)X5f^$u7+xNLua1BG&wd_sf7LLX7@3R}`T zAOeeLv&y~MVwD!08VZyxz9%_`z7~rN;;$49CPPZJgpj<%^ z6oF6ta}|inl>IO4`-_WDbUdx#6E#C0fKS8uObqH*wv zL?>`8^Ejp74ZtTFix*RZ#wb2o^Q}W-6t9K>q$j@wW(jT_&ICOWxyAfZTg6WY_7i?7 z#^z)r&!za$flD0M2FG0pll4M!{2 z{O!p2!wFCSi}=GXl282q6@OTVSsU^EL*Nh3ma%&O_(RXXhCd9eX~iMY`MdJj?!h*T ztu>IKM)QTRl3gI?&-nmjM}qCN;M8z3xY0|+sNwNIj#(OL0dS+)*7@w#8>QzC5dj>mWIh|`LUf$hMv8+J%{(I{X1VHLOpkW^dH$rrwY0A4_$>h zRDmREtOy$0q#;kDbs$|>ta||JQU*b{|5A81x9bE$x=Z&Q{YIBXU(BvUOPmHL-eM9_ zA0(ol#A?Cv?N+wg-){kXT1n5z%1m{eZ4U0WfX!f-Zl?{W(|9DY28X}yFq04rqv=L^ z6z70rZ;PE%yInf1aDF5 zM7y%z+7%iT4`oA9jUtC~Q6#59-7nLM7gJ@%Lw1IM*l${wcNy#%+-tqsFoh7Q9 z0H5TGb4ghDnMgJ%`h>u%xH3Cg^vyrXYSCfEijv{%tkk)KxRuV}7 znaQG3UW)d8tNunY+~sp4aY+elAtC9BX&hiV5HRHCZkqzcyEvQR#FCl{RTTXm*#=@4 z<(q4ZxfL$o$`IkdiB8C>&k-O@yaYGIun?(6Ubc%alZPpL??)&*&(deo3VIW(#D>%T$`==9 zZ1hNiRItT(jm1Ca3F$Qgbjt!`N;PA6Xu+1AX3Hk}D^}YJQLmZ0q~nX&hs^4H``l6A z3Y2d*tF2r)G~??>1gm%4fBkv)l<(%Ez8`NJQT?pG^fs3Y<+*tK2sBD>by8oB$6H5K zx5^>P-TYr_17E!L*m!%Pr#;>_)Kce{dQh&VQrp*?p7lL>YwrKbWygEghtT{n3@#{j z`jh&!O^aM0L9x;Gx#bi)vby!Go+m!eGxJ{%>u@=7Y;6GhwnxBnp2qr{54tM5db<=y z5R9iYzJ7T1dZ|gox25JeLa4iqijL~Ibby?U;AHy+|NA?l3gZcH1>f0_pW+5-JY0&O zYD!0UMk&l8Ohxr&W02nyo!~WYUta<6rH_Nw;hn>#<*HB_ra2IVeO~fTN8@N*J1x)S ziEo4W%f7L!yMl3(U^5}CN4zpZYG**T2g(^V9_GnuJXt-dSo&67oL_!pS$Ri1KB77< zO;HyY+LQ9DCykc71Fn1RNrlyuN)Ea{%AVw{o>VH=+b5Ory{wGa>`9}5n~)GRHlsJ> zduG8RKBz7$45{9QaML%*r^I3-t4)Wjhb+N=k>vwEUK8S6(l#6nJy>WP{%OlSdZ z-y3uYB2eC8@?Ow+k#12!%$`!aht_q58JPp#nJ0?u?lRi`-n0}kq9Baz7QJptXI{}GQpYOWzhKqq5|s5oA|dqFN;f>BT15$rq;TzB=#aEz^;A6c;*&d&4~yPa^W(55{u-* zY<;0<*hBjIGwZnYn=#Ce*7`FNPvq!gO5o~ixxstClKVy6Z_m5EF`EzppyOvJZ{C`? zEsvun0)#F`$Vz-%3YCrI#%HnHi0Vi@rQ0Hq*pr17zeTmMbLrHtgft{~CiDCh=X9ha zCb;WeT0%(oRk3CR{+suxyh7foc(%Fqrd;A7s*)Xc-o6Gn^Yiq9|K>h=YJBv9*SM0C z>ZTt`i(jSI_9?sM$>ZoJ|IOPHJE>m94*$)MCtlGNHSTD`%9pB$UUOw#)b_}kC6Yq` zv!rEP`EHzppKVKJ{oT{Nqu!WMR7cr2$+-QF{vjO(`$;bNL_z|-)GqXsaZo{1j5E3b zxB~d?pRn4eZq?0CUtYC303E7dck;#tjvm6SI4c zmGM1+@ed<`fKq-eN%Y7-!A~=d^mux8aZ=7R!p-+(*XJePVO->WUL_<`zb*72FLp0L zoOI`6VkvY4()zS(ux6b;j;2`3%J0boT3$D*qP5pD?jO;2V|G^ks~S{Z2B^H}t<<|0 zIqwcl)IbzHCnhR5hf-Thb&l-5ET8ZLzt^b!v;6cPpjF@2nR2Bhe1pGXhLx434> zwTJXIoGd+Gu~gA-YDr*6fT3Ey)=w$Xt_#aMR%M5Fc7!X4u@83fmyneabJz}$ZoXgilQG{n)R+*MypVho8nCRlIytSBQ^=W^p4f;dzr{)N z^q*5Y^bi}dk@N0OQ09c4Zsu6er^HRGX=gM)Hz6o24szVK#`6BSjAx>!|BOd@R(S0U zD|TP0ADamt?^ec6?OuC=5MAh2XD+C%kDU=cy-r@BF}=8g*L1RQCBCme8tF|IoknFW z^@9X&S+P5Iag%WYQkpC>(Y1iLt#`n5T@h8tOBuELT+$cCLfW5 zl8VjAqNngFvU7Zg+(dJ#vg~I03^WMr)I*Mm|AdT z-FMbM>=Mq>7dzzz)&a_TmY;!To${V#B~PCrgd$~nMS(6(5qk8R1aqXKwLS0A>m1R_ z_%TM{_Pn*6N;fs``LY=akY zq`RxX#EOoX#wW2qbmX4idtuTD*m)x;@q*SN*;#dE7t9`#lT~-x1@q+Rq`Xyczl(rV zvfyNkHNmNpVv#y%dDAGrz>{$t8-HXF?i8;K}$UEc&cz#svb?td~U+eT$d){|BV*Sv! zPOiOFo)}#lrm*ZzGFg>KCM$aeHszqVbjbNb>d#hrb9p1$k>8$oIR}UwS$l(t5<71i zXLZL#J4)K~KFc8#m1XCh&yg^I@uL5@b{HWv-Ie@Sv`)zDB}Y4=0cV;2_4`vW&D#{7DDQsHp81klx*0`0+($PjF#UEuWjr9K4;)k7Qmj*Iq5Hkn zQ}VgiV&(UL-~Du()Q9(+SK6@0q{?}aWmuQPcP$D^*#k4~Q-@2A$#^KxQgsaD!R9L- zu_<`Xy=_L_aYh*{-1EJ1U0*at4iA^d!B%349%?iRvoy{y+2div_OE7V%|D@gJy-h2 zk9%Fl8KgAiea5-KOVFz(lQT%6dWNJ=4HS?IF(c3UbB!~ydqz0+ z20FuQoH0z7A^p>3HFmrGJCBZ=EjOy<21!7qf2+9Ymy6@%VhLBqaiv7Acnxo1LH++q ziEj6%I`pKB@f?{gRh4>4cM@kvcarXekXgmATq-LLKG`o~Iqi<9 z38V%Lsn3m%l)npH0(~m1UP|1Su+?eVV!0C7DlMzMKA>ihV(W#Hx&T%rY&zqEP=%)z4=-5=eZ4g$a)dn4 zTYEh3ctPQ|dFzLBSSW{uj1sA=Fll!1PyKoF2qG0Z<$CK+l{NJAo)bX*&?eKKWMR7A zT6lkU#IwOnb^>5K&bQB4Ecr-u+ru&NZiI`EBdZ3PHEKD|%&MVfXBGpR z)-+$H2RIp;xRtAEO;hV6Wr-8GiJX^GRizwsvHO`vY2{O{HEpgRnRthh<%0QLOT>xT1Z=0@zRQtbfMp9ig?^?tfw9 zw_BXmFbBez6Ed!g&oO4VrWd~(7%>*V<=>CLOU>l-#0T2b_Sn(`~mKHqQoZ4kb4N znNIWW6*&w0O3OyaXtL50_rNxK}!(qp4_VHx%Zoi{_v^U(_yQ$SK%T5YNYfBA_Ndo5R0U*Z{=NWg$epGFGO z*`hn;S*o|^GBx5No=6-9p}tO|cVeskj<(mQyKt9J@`Ywh5!Q7-~cjcc|-A zl-n~5waik)(eDwx`Qy}5ZA%tC#P=omd!Xq{>yfb3$FP7U?q(v}d0W0jcl0H-Dd1$x z4PX5_RqjZ9o1*#F&;*KCR?my62`~MuMtoP^p$iKW_fbZ4L^J2f)FZBrk8dwlbO(h= z6k@FK`}}QLw-OU}qD2>7H!_)b54V$T*rjTAVtR1CrEL$olbtbBr!3^Do>Lf($@2JC zx3G?6(Fs4Mr$>B_tBmAx`upzdAza8 zePb5LXMA)o8_gkB5Ze3O*}^!U6RMvgJEh4xZ>$s>VcrUENkM1R7wi1Af!-+Jelyqy z-6OFPS5RoEqI1g96}tC&KFLd0@lwzI+L8taK^xPoo!cP0GxfuLQ&<+Q&w8Lee^;NH~`FFvrl>ANffXLdzb5tHNhdtjGu$~S+Wf;*Ph=P-2G14dH}a26jCGVNU(NfaCO!G=FF4Zs zj3>S)nTB)*F8CzhtLA$FL@uoD`jgH<(P#TVImq9K1#rR_G;ioPxdv57Iqv_wHFF`y zotvSTy&Q!rn0Ou5$(yaWwnv{I2IM$w=aVr9AuL|MN7e)6j|tkejD_Y(-XP_TECpTy zA(C~FK-WDqJUWIy#Iac3=j0LJu6le+jxky6zq7KlG`&iU54K+@b7zNzGuh6o;qkUd zySE)LrnyJEhhjyO-xGih@S91afZjIO8_A;O$h->n)WUZkO?+2O;`Uvw9)@ZC=^|~_ z9qTy#drO7IVuEg&SR%$GMTQ<>-3xWEL2Y#G!*H18{N^&+%MU}xiFWw}%zBs*=dtot zORbR)VxMv@?x#wRHr!N_#iF>8&su6KR(um9V&DD+bkSqn=NidI-D|_t&ScRxc}vRt zlN_Y@YTAY+UYobql|hEFDZ3Lx=oprGwJY&c40CiK+~g#(;9*pyX-``u;EGwf$)d&j zQ~!^>cY%+px)%Q@nG6h&VS+Lml*kA{qY;e;G&mqLkmwniXsn`9QGx-4D{4$;cvRHj z1kG?dxAfLpTWxFKw)S4_wN=1Z!YhFwPpc?Cf>=Fav^>=CQs(zv`<$6f5N$vA|NHzu z|Ihz(^Z88nIs5GA+Iz3H_F8MN-J=>abut-y6s!Ca@g=e!b!X&ML-rVLHb%f^j2I$? zt34IG4vd^J6C`FW|M8~uPilk7VD8=mY7narPNGGqUV0m68ot$sI{#dFMNvX0bmQ}hl z@n1qOjjK(46TZOWt^t3LO~%%8Ca|x6xEOC6_ASvvqv{XD1D{3@4VvF)IjRoZ>cy?) z!re=kqlX6u+?Thk##?S{P8v=MFX9WY1A4En@j|r zqs6PeNGin_OU#}zlPq38no-WHrBYT)%1g|YAxY^#gOiAa^v{s1o-X~4kNWaP2`WpVq$cz6wAyGmhDu>^ zPo+W^9YW$;cl8Oz&g&zakk&&4IqVfePIjVP%7D|w`F+~@FpNocGw0fBb>=_1u38;d z`MF8RN;0e(knhQ?P?l*XA7k`m-5K8-PB(XIy6f>7(wp1?#s_LSI&|~hPJJaw&-kCv ze?eE#qxq4oB%jM?WAaH7J8~M&gbH~{auf&eA}@yrR?l7PIZZp)1<-v!HQ-qsNcR$h zbjRIyh&fhdpgYe|&Xhrv7H}r6Ha=l#X|cXq{08t)^D@Rpiy_@^ZdOkq4Aw~z%N-we zq{%a?%x8V^Q;hFZ*%4Q?Jv*je)?Ja0Wn-pASZT}R!?Lvp+p$ip`BKMxS%9m)IXPQGcDZccy8xPo~P=_yir?izCG%UmFzKs;tPM zr02koWn|k0?;qrmtwgS*0{eIJ3aQJabxQ9eF0#rLKl1b6%HmCWen(1%>ukYQ3$qGg{4v7{>|hoonTcwBs(R^%#izMqaN(;vrbBYnG%Lu}lW!IyeY-HB&u znsh`EvHP9zE57VP$pnH-axUtyWY_P2I5jx+x*Tr)bT8Q=ae^!3KUl_N0&%?ri(L5}bhhiQCbtQ)Luvi|3@bHj4 zK$b%Y%8-B#pyf#m<;>kB-4S5Ne{>{?)q{k=UHi3#_cQ_%3#}B%iWU`h| zFwZz1ZQ@}$wN=9&SxJCl^4A&dHM&Iy=+u!u5|M}s5 z_akeej)_Zx$-XiseV5vs`?4I|uapwad_QlJ!VV`L_nnXarta`iN{Kw{Z#?D=_u+rP z$Zw>Oe(SDC+T;vmhyS8(7OuN#8;xl6dBcuIU%!Y$D%`K)F)PD3t~=Z}XnN!cCUXi` zN{cbJNP@3XG&R60O= zAxNJrwSzy)NfdF8^M-xn`r&VL8~!#)=&(9Ez|bc4^bu)V1{kE*$<gizV-8Va+Gh7f{gJUOfyWLeIMdsVSP-l!ou~| zjgO|4O$l2AXfrDId8Zg%UvM{^FsCq@KY(HFx&TNUlg3j>yug5`!2K&R;8YyH76LX5LhR*}0nBZ{bmW ziiY^a=uYqYkv`u@d*b)z#>z_9D@RdR#d8i(xhhx3gPc{Y;h?BMllJbCKthM0;KUCvtGlMt}{(zd)wq$ z%fE!XMD+do8;R4#jSsW(u&4%)BwPGtqq8ChrHA(RrCBHqIyv^2rI>T0pPD|3#Lvaq za-i(u@N_Nu8rjwc%0{0r+13UcFB=~oqb8oFzh=ClnsrFf_(*kR9=TJy;Ure9d?H#f zHi1+6PDAWWL-sEcPs^xO$a-FHOe7AZpnt~m#XQS`VVNN96d{rlC#9ge8s&`0QIPz> zB^Wu(c*V=Wb@+OKt>HHZ2M_vTF*#~CKIlW4;xU`!(z5iW>P-@-7_Br8WpAhH{WPEJ zl$#hPhoS6{qGEnz%A?h{1MEQfrn6ER>h!m`Yds$fS;X*zYvs9Jo^>;Rs`>m^;w4vC zi1)7d6^moi(vaBFs7)Zg7{v6kzp=bPI2@M73oJci1{g<(>BWYp=rH_|SwN;95Su|< zT~g59Qc#e->uiL#Mfz#cj%1f#t>-b(P?j)AoF-)cAJa-NMKCD5FzUb(kwTZ=I)EN@;@TJBm+t`loz1>SGQ~hErchL&}Q{$^ls*qb+?yHJoAwr5>(vrl0Z>_4+0y z&@I)rZ_;EV46oD|!kjeo<$djKIuPx}OaffjZsJk~W-jpIv&%`$O!>Tct9ICk(X6RA#S>jFmC-y`z;cDl?g zm9=?TbY|=)guZt@#IPW8{p3n;?n?quO)sXsAr5m(KlPjQf~7`fDP#0nuyluXV|z!) z*Wq5$CTWY`Ra zJ7m|ztq7B0^ZEvbzcp~*zn<38s&#@WPLaD#V(55+qF0S@_}cMVkdEsu6r%)pL7Xo! zZ3D13+c!{DWt=m5;!WW;GZ*PnT$Cy)XJE%5;GQld9dZ+f$PL}EF^}4rV%Z)l86dKzm<%rvW46i zidPt_IpP8xF^V@HLjESA9s1= zS>rjSB$AX$uC$j;l@*!N(RqgT^q_R78rBr3q02ck%{q(@$f1NUFxMp1Cz#>t{Z@IV zo(_KC#$D_jQ)Rn;2>t?Ma<>SqOk@}cw~;8=mTQ=sny&+;)^3@~v4YtWue&Y;xf^QF zpu0x#jQA#2FjOL6R8!tnH&p;oFi4`FGNWdiQP&d~Fyx~Bjuli%q{U#d8@xw^8*$fn z*{F35xm5N@A}c(~I%2A4>f8-CaTbioH`dHIQSwF7`F;Tf?gdAqKuXf91yzLEce9Zn z76Z?1PuPrXkjAo&vh%(~3C@{H!fLrTF@{M`QM2%{ky8!(Cqj^RApd1E0$KDWlz{lSI!EdvY)gx#~EnO z;_w&@JN~M;fa4RPV{ehOL~;(Nn#39t-dE5xK`s4PNO?vxuicKz>ES`ip5HLenL&s(yf4N&YrJ>OVM_gFvo_c z$lV$7KNaSg_P_faG7}u_<;=fQWN%BvTsHuzKOmO#Y+sDl?O4FjO59eMwztL)-!o$> zv9D2v!4DE4;`KTqY#PV_1%hA<5LD9h1^ZX7n0E?~Ag<{-UIM?B^!%N8Y4gP?OJQ|a zFbPst!ovAcYqg*vkOqbF;FV3oe@j+`)(;pMpTCxPks3rKs5|J-$ z#_CeT=%cfh?n>O3h)BDsM8-g}1%lJJ!XVUYx zr~tZsr9@Lg5hCYpL|Oa96Y!$vA(mez>@)OLkAYBt!TRtS8JPDZNXL6JBasyKG3uD9 z%ivfw5$9)c*@{I!LHQdIg;TyF_9BngPl6LS?uT?rDcQ%B^3CmRGRraBxmen{FKMHM z7UKVvc9fg>-GV;*8OH>#k{jQdy>f#SEHYcA#VWZ=ssi>(j8J)sl+O_a1{zbTXC(Bn~KiZH9}=?MjWN-Ql67Hj!&t#*fNpHoAFA1AHQ;< zdW%mfniXq)NydXrJg!npwlZIRjXwD#*=)up_SWzLoWZQ15mTtOGT*pIHr&xQRiIL7(|aQJy~`MeE&D`I72kXRP8w*@|H#lPmkM zo1{FQ%zCiSsO)*EHvBy=HAZRA%WUJUo)^s+((_Vfc~`31 z_jv~dUJC?*oAQl;B8iAsb`p6AxO}K0cM%C(IMyR_HIZ|sY!=YkGx#lPJNH9HU2jhS)$EY+T*fLjfnik5>}%W>kJ7^l0QB&V1teW-G62KRkvH~|bvek6Xrk$9aVEJ)!4 zseZ68r6JQ+OYA^mjXPWbkho?X=mv!ht;%T zM$_(ulcs4$@Jd3{BK@TiF=|Q-kf#TD!Yz~uZ&RuOGF6TZ1aGp=z9PS|4}ioRzq^bP zu8?7nyi2C;FF(<(UND+I7UDjEv*Xb+qBZHFwRTa_L*i&BW0PV%$82$ zg-d1JO~0mvm@NhMCl1;uMaUHX;oc8_H_?Et(!r_=kF!GPO81!w zxVc~_O`Skfg;ZH4N~(?oSK5n-l2u_#xSyhn<0KDI|~OIW}R)1+$Z z73z8s+rQ6!S+tBbFlQt zuyTNr{sf_?``V&zeD4ggXC2M4O8H!`NVph&kh)b7V4)Rtrw8#yl`t z((`Kt8Otc?eCa{PYR<$Prd^U0%i4u9I2N15?qO0I?+UIluci_~RnilpSf;FHnDmc( z|4F$OQmzmh%DvDm_g7NxlH<$$oEG+!`_PHY?OCU$eUjYCt@MGjC^KaOwT(z)q$*$8 zCqbxb_ah1dy$zI#(zO70Yr!daXRHU-kk_j0baSJ^3> z_DLR9Um^9Kz(DC|-%SBbPwU0hLjNW{ugc`}VDsXx5-Nt1m-qkjH+J^ z{|&1C->ANQukcA)2hXPIO@)m-3Lgl=>ldTD@0o7kctw1~&8d8rZzVhnU&13SDKe{a z%-6RkE;7$>m-9t$r$h>rR1`>pA}PLBvm0;yuTLb4qWD z1A(|}Hndu#qH)iyJeht{;*KkMHm1nN!kmZZc&?#*IjgOvqEMO}ke^!!G(DKT=%ZRb z1yb-(X?NV=lIr4~{!;!IqG~mXnW-|C_M0l>w2C;j&x*Se$nV5rR!Z5)Go`#kU_8@P z3QAFC-5HdE+L;lSL&PGEggf;-dkL5`KK{ZA5&x|4Q;xz5V1n)KE4;j72=w1>bLrrIL}DqKW+Cz5Rra4GT2!;VLIxu`_|nxaIheJ0j#T;fo$U2%TB4c|VV zPu)F+Z&ixh#_&y%BvU?;#%CpJvtU~H-Hk|n*^7`%8%-?q6?`0kQ@Km4J*w%B-8^eL z+7*agc4CUI4aNmacWajDNnGpH$K1olAaoMq%PuF$Y$ex!nja2{+BNB!Hjj4cuE$cT z85XB9=Ps7zIWl0Ie*^EyQfWilXtcYznC?)x|Gwzt!FlXTFpHTq7&nbiWrZF4 zqP2s+GU-5xtdtbQd*mI6-8g;4Eb8(DB;yW96=SN*kA|so22c)18#;^{RjF~fRBb;+ zS#C}~#Y}a<0LUH+VWqO4s>^5Ju2iso6m2ydZPA)`%i!`I%2}rA<7Wu3SnHTVfMd(H z00du2)q}oc;d?ZFn2hIa`Bx&;G&=$I=`h~hDVrl6aANJTruqq_}aqfGD?Ms zLS(sKQf)OzbgJYVz;~Pjcu9jT%A2Owybj|Lio*oKnOI^D(j(GG28nXr<&-3YRb!$U zOSnBPrL$B+Wj5n%#;VomLtR{N9dsIZ;S+h^;@ZKlLOB6bY!U-!k4?ggQ&wvlM!oDJ ztb5?%T65C#tFYJ+bk8au8!o)f-Gq>g(Ju!W;wnS*?3iw7@w%K>ZC;-3?~N^UhssbU z?4k9(@#RQ-+*gkuPXMcWt5{b=eaUbI|Br$U)tTrDV)rJq~~L*GS~2HEbV?1 z&22djZ^Up6J2K6li}9FUiv^07_Gu23q5KlTwX)~pLWbXpltIutQieQ^?BH;&!OJa= zpm(GVnYdVnHXl(AM6XmXQqgMPhxs(u&FbiGjMk*W-BdWV6`zMi2+lE9LqprIadKIs za(cCHQrTjCv#{UXKo{sxX?VOPn$nq zqS19@GN7)ilJiy>v;n*-YdgbMPKnwKgXK}W6Gr+9I3dtYuF(w}VZ2Le&t;iOhJd8f zULn%B%nQR9AAgT56+M?j+Uvvpjpu~>j~4p)hgsd?qc+N{e+x{=WKHQqQ|qwT9E*NZ z1>}P|o6rN69o@;uA;Sa9%PxwXC57j3rt?A*Uoh=YL|!rY6ggChF>{CX90gHm?6YNX z;SrCr1;E4qtXSU{iLiD3hJWto2Z|W2%$QPQv#Dq*yhfQzm9tjLeI55-5b$*^I#&YHvxF&p>FBFy)7#qEY&L<)KyZm;1l^A~jb-AG0X3Y76;m{gDU=v3qt`pnUI;aL zav=4YdDNZ^l%)$FSg))7<{mvhK2btFW5>@R zGF+@!ZljLgfosK@uaanEs%oPnz_Xd)*Cdb@#oS{-*~(0kiU68 zlllQaT462^UMv0rMSVa2<1{W%>U+PepcKW0HnP61R0}&}vl0KZYJpPNYrai#I11ae ze)J5o%w*p?4-hY7Ym`Z(-h`*>7M85G1 z(sq4yVup!xu3>63J&sCIUft0jG~?U1$j5#+$<{JBc4#u3ofvNXkz~PGj*A-bp!f#eFJFpIcCe(5s!t-+~z_3Sz zz?q~5``@{Ys+s1y-;`#nN<*U|tnf}|{}pY~YTx3jwR&H{Wi+ed-{@N?KIS6IiGDmm ztaDnAL1jMf12XUKE-x#~3SXf`7nKzy!xN0x6{D9GIhzgoDaAX&12|Z5`UxnE=z9*` zDI_naTZ@Lsj8V%e#xJ+Zf)y|u^LUJUMK|{iq(R9~qKA($I9Hl!joZ@M6IaoLi+rv{ zNK+cl)oMl4T6d{Hi~B+>Xni17-L2i_dGr8*Z+Tu(VyMA!TB+7JHC>d^V#p+>9VXf^ z_eNq1l{zzx!gTQ`jWilO)lf_&qEtb=S$lxxbY!e3ZH{qcXs|WeC;AbUf7FNjMnW-! zwEA0>_RcGXeb&oMz|$35lAisHOX)Vb4?A`;7lFsFbyoU5cSmPH4a%2Zz(_nriL+u; zS|;m*k1Y*dG`_nvCw#`z@{2BO+;y~>_dr@j+g#CE-hAh-M1}GCaG@gZl{T6=Ysp|O zd`{5!r8~NpMyHZjwt(Xdxw8#U-QD!L^e5StINelmDg0O2VqB2={f$wx1Zl|+7sgt+V{T!-_-tEVHJDjDE=1DePA0;?onuXerb0ho80t_h z=loXTrdKW2xT_QL#ewk&LA$}G3~f=gHivXMt+1rl%GOYeHx*8hYJ7+4-%NqCywG2KC?t%A>~btU9Osb!O|h#d zLDfsLCgtF!-%Zi=j|&VwoSlZj_sn(!haYRQm42d7dI*mSZ+eHN2~&P{PXve2lT&cG zt>`#796lZnJ1*{l19qjr;dToSxA%sFJNlrEgq&^{SX}6S0d4NO>{-!6{UdqNE1cOY zTzsDu6JzA;S;9rE=E9?rn+6`%Z)@<@)NRXAaW<5^aL# ziX7i+6FkiVp0&3F9&iQ3ss}s^uJ~r~^n#5ts9j!1YmC8Hrr|f&d(8!(8Yc z7HUCAUMN25e3(!RgHP7WzW}3#O3I?UvclZBW6VL_!1h~x=q@iQ%Yra%-H4QwI9$WbjiJO7{-qT5{@OTH`@8jq%f-Y?HWd)uMsbvWKn_{xFS&6b|wpM~kAL z)UZ3Gr?tD-qsZYIaUA)0Ht@shVVE(L(y`MWqbgBW4)0+SLnU4FNmHqR4%I3zl{xp( zgi8IBVCz?Xg3zo`?9P2x>bb_mrBg2)8=sUMmo=XwCv69}uw{RGVt4t4WnM9Dz;+6h zoNe4SN`&wX#(kZ}wzFlZ-A!501q%Q8qAVLk&mdDtS);pY4}dW4|GO}L<}stOE7WG_ z6wTNRsYfcOtxsfR8sN_tk!H0$^oDVlbM?>*MhcoZjqkYn4K z_if>pkjv0PKu&LYJt*X~AMyCzz98U!;WcB!d7zB|OlT<>gA}s@QOL~cftO{;U`lBG zA(h~yAq|jkXV`YJX6a&t=JOM@7&k*XL3{^&=qYdW|8{q#%t)E@c(Dy zXo4KYlIDMOp#T6-3e+0ImF`zMX!1p}BOk(UJF>8M36Dc_6EF3S`!y2R#8bWF?m!op zctpViO87e6X8Um_w3jI-CUxkGkuS~=I6*%=hi+5N*6-Uv^D4F+a1TPgtn+I%yrh`T zBkOZi^pGQRy3rSFREslvr%fJ(w5wA%dS$1vkf^SeBGivN`l-%Yqw5d%YrO0(yP)mo30+pj{B5q?Nq9b@p zRLi99YfO-5%tDWJ&L67`;9FTwZw!&Xig2~V_-`z9cpl2pPmbasZrl&q5!CeqMo)wj zVn=hPYGE$zF!%44B*2m}X4)NK-LV7H*f1L$n18#*`5!j*nBal45%cD-jZ}>02f`+| zm`r9r<26dwcO(WH%h#pA$p#uT&??F?O+V6AZWQdrcBBmMHF`wY+*&dLG~?cM=0q4= zRqgZqh~?T)1x%ihR==Lu_uxN}Si((krQ5r>(-?uv4N?AkYS;X`crmHFYpGi+ejlhp@ z<#G*&{wCf1DHRB$S#zz2*_Q~wbKAM*v5ehsIG2%sXN_vI2(B`uJG9-mp+Q+o67SpK z{xN2dW`C41O?x$M`AmmjB$)*N4ohYB8}~t~*DUl-fNr2_m0LJEvE#~E&e))xd+FNPkXBTKT_bfHOH5FQ+4AzeZunBq#Y*7qG1Q~gcG2;xMx1&Vo6Dm@Oxs1pGQ zw^C+N``$L~_D&>YPMwKjmA2EE{V2VHqIv&7%&?Oj_2a&PgUW^{Slpu7kHC-58y=sC z__VhYnZ5jM+F{<%G<9`lye=zbKL`tIY?2NLths$Bq`y|lz3QS!Yd8LSu380^q2e!) z2Ne^`4B&oe;|cS_mD1H5W{!RcYzBhtFwT90WsJj$%Yi6TugjgzQZaKlnP|POYqp48 z;q-dhgnjmwSX~?+`>u)=0Y~Z#5F1{mEDQX>p=tiLju>{f;(6x9EGLlmk^-l*!(%vn($;Dwg7^*bt&6<3w14B_OCxyqx*ThtEn!ckG8hyqs!c|i zU0c^;ifin;pfuTdc+`9s1+_{+c*LsD2AdbZq2#F?P6;eW{$lIiQF(eYG~x&*FK_=< zNg$D4;sAghkCL&df|H(~-y$v8frvaAxiYiLEUJXZ7Rx}>9^svvfve*BrIW&kKIoIY zPZYMx`{FeM4WV0dY$vGS^Zw;SH zb<*akjG!o>z7HXU=q?p>T*BBTJ=3X@n3sb(80~r|>xg?(G0WfMZmN}$EB+hPKsKdGnw?)I^**C9z}mQ z8(8=$Q@c|W((oP`epH`JOTv~E3=GPXUy~9@v$(cURIYD zb3LFwAt`|7D;q(;k{do%m^_#$M-`uSzcc2znuo+Ns0+S6d5k$O_xp^)%>j2+W0> z>FHVA6_bWIhLAiQ`oGfn}6D%A+rVGs*QHf5Qg56kwE;F`GO z1q_zNK+a(Vk<#^UZ_Lpui8A|u1!sl`k?m7TPLHE-Q`7D>-T?;Dk-t2T_Q=-oq&CNY z@XVVHdC84Te^2!2SCPSOj(gM57ZMFGha2@TBS@UtB)2(kvA*inxvYGyDp2|mZ(1J$ z%J=v9D?d)YuElSpllxy*yCkk>)YpQ(PXDt~f5z|6)nb?DC?gsz_K-|O_X{nvK6@I) z)>Dj)R(xz}&ah^V=W%B9)2s-R$A6b0TPb~5nlrXJOAhLZ>H7f-7RTAQn0sdBJ2?KQ z!bY*`1I5d3AOBaUP=PWOuM=T`*w(;7>Os7>gsV|VNgcAigP|6_8LU>fxGz`>R*Olkid6TUHU;8|itgEt@W^u?5gn$+X>S65l~Srxsc&#`i})brRzz7+kuKYItSjT z1RlJ`pYo5(g6l1kAD3&1Ao|`J8^{QYRu?L63&o+>ycM(4`;oK>Tq4$gSC2>r0IT$Pyp{WXJWRn-B=?nKO{7K zq*1dXg+{~@g8rNh>naphOb}RnLZ0Qww!=8KTRMT{+H`1=foV8uk&Xu21ZDOsY_oGC zh1;o(xk5)P45(+!bce!AccUmhvm>!?$CT5-9g#-U*13XXQUm`rrN?DZ3_?mV|#)Yp-xrcPuMo{IFW@#AqC(-31-LeTw^Oy`$qQSg);QN4L znWq0j;-rug%~x>msVG2$!d4GAL1V{`AR-c(u{2F-c}<|KLv}_NCX-%^J*?Fz-{3FX*f7_SlRT34jaO_d-ldqF*`cKZ!q_pREjC+z+|< z9iHcD5TNp4BFd*#hW8v=#z66cl}02?}BJoPWa4dmn(d7z_(f4MXpN>2R6?& zFpk(-IesM%@HLJf$U|Qq8hOxSQPsrQz0QOkpBJ3X%yqx8PSFvlM^}>i#*X>pNUU9! z{Fy1mSa*GKe0P8RN7m5VN)RkN)-iJ!|CeR8b19iW*t$EX($00`TLWbmhW-AsdMsnW z@Y#ao%Y7~L3oGJPS%Jkzgs3%~8d%z=!rm5emYVxDoCb%H4-%#n2fAvtd$0>)0J^Hoij%<_q}A+@KARUYjseb^|qHH-K!x+-Gy| z5I*}@M`Ax=jbE?5QBdr85H9W)qj>n^o&$OfZezf6LR3ttU?Lzgl>WxGVsxS(Vi=Q} z`$M(sY7UGpi39~uT}#aL+NmZ=kt#?`-DQXydrf!+ zkT~f%ON-xh$Z{A&nV{4^U8TxIMVTs-&A$SOm|msLG8iM*jc3aFI%q=r{ETtFnwv@v z#kxl_cnifzx!ai}ebrJrPReK5lNBQfKB~^Du(mJFNUa%*eml%#YORmsWl?vgQtBqnziKSfvkVl@U@oJfox{s!z)TZEkAR1zW@0w zfAe1o8>t6#Ja^N-tA`vY_u)S7CNtU@9vnS9AncAFc8BkY2NF>8d2J;QZ9D=5{f1a&3@znZ34l27uzp)E*a3p2dMim(iroMb!6NIa8bx{OC;Wxl{p z24_IssYWFxM7y%Xr9#0@j&}76d!t=_!Y85Vz?I2Ps93yU342UXdAuMj56rfLd*uP& za|Jia_A~k$WehJ&Qh0!Qy7q0Pbj#!&(^St_sB_d|ey#Lmj<|&MBMEy*_XWl06aF+! zTX-(nS|a+RN>2FXOU{0Q_|=qRzU0$ua{IZ-Cc#MjJ zu?h?{osmBoyVlcD^9%wG0}_ujV>P5*w*;n6yVcy!>Yjz1$<^ihruG6;0l`tDCEJ)I zYVO8dezv9L!>P>C%E9rZTC+tm5JZxg}Dtd#|)sxQR7WYYY1dDNgam#w1#2 z$;~V`g`(I;WV%$zQbp*e16)~T`?y#c8A~UA z$j?ae)Bu%23N>sQ@t?mcr9@NRzCtSFw5ZN#A?fTA7S-6I(&%|az1m2GR=nAG$&6zI zqh9~he3fF1Uz*R-cjLE`JpP<)XI?e|c`th+$J;U)#$K@xe~O&ng~pyFeR3JIRMvsU ztrA8b0Jd!Ut_HSHVM*aI%GH;32q)XTO8D_-X3mL-Y}aX|1doz+C^dFYr6FwW@)Y4f z&a`+cZA!MRUY<-QA7E~guH;%Ut(U~lNn%EY^rz}7q3k}4A_c<^qXqd9GfW+#kbI|+ zrO_&Xpt*rEOK(q=Zqj?wx|<#aA_2T~?JLg;X3owWB4B;e(=+v+nxCon)JU1{nKN`r zUu%ZuS~FCVa8yC2C8r}I*dQb{^eed%!;f25e`{4maG2%kYrd`^wt2L zkS8wvlr2u5heiK}G<$#G#oe@fQap!AckCxGPT{3^)mBU^T7r5zw*5I*>A4pMiw{q9 zzcAPxzw%W~ZHCOxnH62-#jv1XJU|5E8zNf40OC0|frw}peR9k~F|ywMLaY0Q4aM!@ z{3Yub_Nm}X_e73tm@0hR?_bKaNnkOL8mR?ezszqy#Pq=4OgJU4@U}(jd;m;_Kxnve zE7(aqrw&diOS?wuSjmf1keT{*Xm)K(r_=Do>&~?#w zF^lRja)>f(N=X)2YhUB(O#S^|5R@zvY4LIT`*JYb*Yx*=MofvOVLbmb2|ku{UXa<> zekV}DE6P&=cmTWcF7KwLgsIw(P(`o#eVkt3WjvkEk(g`h^Vgo-qt9Of0r-Ea&;N?0 zkvQ3yn6Ai3Aa7@gS>Q^KulQu-;pPr@liH}TU1bMr?N~?J8j(zVU8YR|BGL9wNn68W zex$|cvU!c&;@4vM&=u9R?zjmorIoJL;3>4)TWTuVGsmW2D#O0(@RFVP<2HUf&YcX_ zug$kQUY)flTi;QxM;26Y42dm$-*UY!_u$WH1S_)xr5WCCPv3^*m)WJXmC+BJ$yG_u z9SlOKwo^ot_rl6zX%$?KV}1E!F|e<@lI_n{?R5Cp)^L5s>xfouyc_~y=a)DC5|ohQ zVtz!s77w@~zw_C8wrJ7Lea210NrQ!FTs$!@>2^0AfoQb#*|?GEI5eHj*b5P{PMi*G zv%UK+nWF3RSb^Ot(^@i>bNxw?*%^^0n64k_7JhU6rZnmoq>|qGfs<0omZ=k>#FSu` z^f#O!itM|HUT!vlU|c5%x)#ZpyXiJEBiW`)6-x}XNELMwdlj+V!}R^~Z!a2Nmg7gT zh!wMiN%a~>v3L-Pn|leE-_`|oWmUiVHHgmZ1yTIlC~#huhD!>Z{k5J^5vb;zX+c$_ z9!qw*n|{MUx7m+}>Q7!!YfX5gG*nj$RMm;_hHrtD=zpa{-~q33KAoY6izI!S7$~2K z(p3)4BHQO;9DNfDdlqp;^1;hF(Fr19CX$&wdEer(GMq3T9SC~Z{v+HO#NFmS@y`)2 z2&fYOaIYCRDoFurK^K40tcCc@yp)C0Evp64X(%Ti$teH##vPQCcrwK&TIU<<*y`ir zPYIk3$#ShShW&>6nhIM0nM71mrXMpWqC{qo?Jp5!D4B_PmI&TTgvEbK(o6k3KWWB$ zjZ#P@%Y3$_x~j5NS0W9oLmHOH*)((tyHTMj-dpVNUB;eYrchpDA_(k!pDPEX)o_-1 z0^yLb$-Pf0;7#5Ae7UnM+Ue(dV?O(fDp@1u zLo;TV88e%h17^%-Gp3Fh!;E>^j9Ea;duGhjDn@Sw>dqU02=?PsjvABYRoDHz8ka~jbX}9@V)MC5o`=bET(9Ao!iSgzJZ8QPFyFSw z+Z%?^N>Y|UJ$Tz|zAfWTs_K+iU4N2SV0=Vc3jA0@ z7-i~U4T`zYiRt3CmkM3vODBT(g*KJ<=El*1N5jAXp_5X39;=3j~-Q*VNJ zJO-b5I&BuZD4j(tyRZj)TRDxL=(0_{;fb)fhVdGx7zXa_|G;EN->k&QbV%WBrTZ1y zxOA)%37;0yMCadxi^sh>L~1YND zuyUESOo7{J82~~^9Z0@`sN8+^dz$?V&3AAfMv-gfbOvH=CH+1N`MX>j%4Kp*Gy-Mc z^G^5kgYC_^_GYAv{wyVy*`pPI74#+N|3^qy&7e&JUcaeAV9hGSE)ipyeD3v_M)={Y zSgB1Y%dFEI!fB~%GBF3-C0nY~Wbf)RUi)c!A2SbZ(6t4;AP0s&s$|c$3ybA!_k%AH zr^TKqER#}We<>_c;Y)=@grV4<3cV_PsW4x{_;-_BbNE%rF7jV-`+=@-jdl>Lz`86A zkM{G=#kLGU1obyLYhf$|Wd*SbaW|h&Eu0zo#Cw(g(kFSmBwDqt{}3l4?q|LT+CNWv zT*|E!2BjMnWbZ#m!Sjuf@1yV;mHNg|@g`Jz0r&H{g96PtgPQwm@$Z-*G}ORsx>!a? zYVFC+d!_GVmA$ff^S5UIzL`BWuJ@m7b;5TrJPgmfWMB5p`QHiZN$xZBeKv1|W`7s$ zM#JfXPF_1}9}6{f0#^=GKr z`K8sGJC0AHxO6NaJ7g00S{I$gVrj^`D#kEh88e3&`&&Bej>>WQkiNUCosfuzyenf< znG8Lt$m))6QK^z$zfz$&_cYy{i<)lDP2HTUA>w6DY6ypA{)_T-4jXd}B4vSydwT1f zoRa5MpcK^KX5L56lLf-RZQ;SjpMKoqz4JO$3FU&%SD6To#m7ZTEEF-k4!JSe8LO|bq;VH4hmxP1eWIHHD_ZCio)w0Wn%Mb;>^s*TqTF$@EDe8AB)2; zdwKIoB9D4l6)^Df=Ie$e%j4Hpbu}-Tgc_;PvI?&|t6o=~j%dn|TT-2kP%8 zhOxF<6{+K4eRLO`mSNxiLDO|P-pDY_vn!T5D{$~4f)aGqegXH9Jem+)Q;pSWOQOp- z73hPfux0KNe5Y6~fxTa;HxCQg2OF0@nnp>x|5F(5#BBoJkHsRM&BYc|3eZlUm7zy; zp_ACI;@86E;X~}m{Qj)P?sDgl9Cf-{E5?^{mR9^0$xb9DFeM;WwA7#4I~XJ0 zrSA2pWgcne;DpKZo3gCP5aZDwrQ0}xHY!cDBuV+Ey$ISUc{mq8;UY_~izo@D8%%^a z`$l2O4Wtsrs@S$naz?d$Ot$59s2O-AAQ2nGNzR9>27}>CTR0`bHI1v#lqOpm$I zs#VVyjJlc7-L%Bav*k1QN1x4d-|*=y_XTU+4_%;QcX}Qs%>nn+k#4j|d>Qj&LU+>+ zYNMo)UA&3A)De!mNf#R7T0juox+`dJrRKZZ$uV+?A;#8V;`SybOP13)^dk5u=8eyh$?ZoPs_IeLpW5JUdi|sg6z+R&Sw+G~-sP&l~xUsy1)b=Y+)Xqu~7% zxSr>&Jj?F`etU=;`J~zGQOnJyroL^n(vc?5GtR8z?=+tV=8YN3J7qEWI}6PGQ&&?~ zCvjZrUbN9FpN`$QiQjvClTNkr<9ko17@;%GZg_^MFmDee zMv3ZkJSz{Iv;mGcd622pGHY2z8x{YhmA0(i)v#ssnq7zQ=jmv{i=!q&twQublx!24y^7oSGK{;tBvwLkjvUSmRJqbRCwe5wVze5r_QZwKIrn7W#{PSUyNq0b;3dB23aZ*-{l-8UB8D(^?D*YE>0 z)vGgFDp38td`?XDJS^eXhHazQcJ1`+d5D+8MXT0sJu+&b^xz1S(GQQQ=11gwdg7Wz z8s?)I6dFIp{2`-UbNQzEaYvrHGk7#_P_3xAM|VJZ;Vl&jwZ(WyW6t3GJ)v+4Q|s_*AVsX zSi;M)J<;%P3%U)%*gG(qbF=XeD zwFU4I3wvm#zGjLTtX!|fXRl|=w2xfwbLxPLqtc-JV$oT#7uSONeg(1>jl*CW#c%cw zBZtEEO3s`JFxV^#pyf2W&ejwtvQIX4G3SVl%!d>7NAG(eJ2FVGcs1I&o9~flqV4ur z#jE9d#WXqcsF{`)va!V|_ihZ%1o$XQ=5|&K$#Hira%#DO$udr#?0%>>AUkW<`0^G5{ zLJkYvtfUKywNU?*9wtvtqA@=m|MnF^9b9k)^j7z;VmXL&^K9x+`-pa6lEWF!Twi*$|r_&0LhW#T=Ez88P})6UEWG@r}IjXPoQM1dDuG1`O>(f;%78x z1ri)q0a*>2FY~+%wd@*_%x2gOchUZUz7<**z?30;E_UfzkvZit{|@&JkjOz*Tez>6 z(BIzXZ#k3vZGLqGC@~8E0V0}{^N|X8WdU^{lxHT4I2RZ$si~G7gW5?fBjjtSa6yQU zEHIt}%I>0d?jljSMAyvDWXKfMGU!?K6Q}#Xh8F5Mg`{+@7Ey@%zpg8Eu6F+y8qXD& zIV8_a$IWt|@3`p+^rVC3shX#G{SRpFQSJOYw^hwyaEOUQq$TDo;;GIA6O%NBFMw05 zIxltKsTR{x7ioB5yaRX?+V-l83*lndTEmy2o%4eK&m!~_!lSoFSg91@ zw9CC$Ja~_p@7W1#!sHz(nSAFedGq~C2^N2pbaT<=M~=nbVKy8R6V*=@gz&E{s(G`b z_xWvjupoXVdhO(yRSaQ8i5!Vy;55KVnOY6ziWPZhR@F$L4~%J52~FS-SdNW5N^@^u zM2p;i8c>;)MO_8cBQyJyJbhw~m29FaN7GMXJ5ptQ#Jpmw%=HIILGf>s^voD3;H=E+ z4mk5L{&ue{?aQOneC)?#j``T1$87U)0FQXDl(e~eRA*Rp} z@Em6Xz_pe^h&TW+PLV|Wz*J>=$HA?O*FVshOpa(Dadg0D)_Y0g_>l=}2#@S;^@qL2 zVU9;8PF?57lSoi>Np#n%#)m|TxoLu1kzO_4kT+$QOK(K_M{lO}_^32~$9Ebxfa@#e z)6HGKB|`BChAyAD`$}~uIdU`|O^&>b|0Yfk)dt4FYlQ5rA$=PrA^yQQov23g(2~yy zmkQ+6XXsVAA>X$8{<`M0ZwUFe)(;HG;h>hH3SUS42XZ-2#$S3r?-gxKeEcVB25(k( zz9CZ_F80PE8Qp9=$TSo^say9Kwi?3@Zi#kg9cKU|@YP#Wcj>D8IW_Y-U9RUHTUCLQ zGk4{HY?!UF*W^RlX^HSfl{zjg^$pnk8=y3svYxVrlku$JGAb}$bTX4`w+{%A@O=eX_GVV0VWRD+KJQghTdrt|`JK z;N~ub^APaL_x&;%6d|SQ@f*u z0$o3qoNYN-41=vFUU&6>UkVQ8cvF!eB-dB(S&?M?!H;O6O=dyVlHD_QCfjGxEf%2F zP3!KDaS&R%>7=10db{f9<5X|dQ@6Cfh}J(S{i2^mRzFWN`+0{1F~>WfOFvfe-|X8< zJehr4XFa8wD}8%~I5@fzEjF0*7_Rppp{}a{wKwv_Cg#k;M^d^COx;Q;>0^{)D%fEPdFQ=BxTfA zsy;s_wTY|M7OApEnZ=)J)i+ic1JhyBY^krNr#^LuO`Xi4p85_HFyGpu$?QdcRyCS- zv*v`(n9tEqxL|B>VTeXLE5&R6b%pt%;w_c#iQ8v}^r0j3Xi%kZ`+p9T0!m=3dO2nKkkLfEhy3_kBezj z%k?|T%JoTA0T{3l=Qs69CHfts11oJ#F**wjcr83IX#WzgfI)Y~G57P+l2Enkz=+py zH9R>!Iv>tF&;8(T5C%^}-4FV~(p^vVg8?5V4oL(0SFImA?e7OhyrtRqleH>7^yc8= z)#A}-`|gl^XFxwve57@+!~SOMQ5%#tz+IgjL<{KGI>*zotcKjyzdHj7H_aqFnIAS*G%~#pX+1wNV5pYjD@}eNG=+0#E;nt+XemKCA*{$QbA%S_@f63f!N6*JJt`^vXm!(3u4Gz$buNs>$n$UNG)YS4F5Nm^8aK? z22OwH`1uGTEpb2i3b1}vMmu`c;huO@CILeSG7Z_^FUJMp|02l@f{;1x|8FC3z2ko} z?$MnSGR8f+n-gT9qUhnX-4Dv0+0m|%(T;4H@U6QYoab{U;Fq=<`d3grZLd4{UUGfd zYu_1f$WxfW9qp9KkwGaF+hl$A`xZQrLG#p7fqW`#DN7|7Z^#X~Cv8eM@m}tSZ^hU9 zpl>cPlk6>YLX>+Kc#l&RXcE5I{re~{?_X};JFerAytzc48_PDjWvbxD$IC%zI(hAWV!CRY-E(l{*}X_9%I$lK z-=?$~!QwBq;=>IXH^Rni_QM=;5^JW1e@IT)FZc7q#J|_Uw}Z(|y~!1Z2jRi)VU5&j z3p5Ws#r^!yQ;Caw7RaPyKcX0mjxFW!U!r5t@%W2K@oyuh7GC!+GjP&BIb%IL5MN** zz!kS5Lj@w0#h-_Y_cu5~xM3~20?VG!ftSRRBhb3bUZHyt4_m585qyR41t`&-Us)p?yR>4&=ab`uM+EI}=(#_oX#39pS>ea? zaj|jr|68=lCR^+K{}%xHHoV@0?NT$&oD>#|nFhlNILr`eWOlFR+0B;4TAm3W|Fz}$ z^Do4DDO7l!tgx;h9+1VF-b=x#8e)G1wgJ;1FsK8=1Q#c4pOB0#R!gD; z?_0CD9~=cq>)ixcXtH&6!wrl6wfD#S>jWBgkkN2C7&)vz`Xr6~);byrGMc553ufbfpb#g`w)p~qg46tsSkq*|mJ;vywF?38^) zwChawLp5M!ScC1mgYhRG&-sYqR9aYphkkMh!!-N15WpTjDHtC&L~~DiKQ(&=UxEzN z1mA}^T6(ypvOXU2-|8a*=_8Lx|PEx0x`P;3!_m52Q!)?T>sa1grQ+ zmt31FCy!>RT|?qzd>lshqJ5izlN|&GMoH<3cI8s8GgOSkJQTYJ3DrVxEKm}gGdjQ> zS>>@nRe5Yq8RU3IY+R~W#-x1Zxwa=6a;jYHOB7iU+gxU7KD z#nD7d!t}Lcm2Sb^@Nz4>-U??}VWSm3YK4Ea z!sS-D(F)(TLcfP!&HB%^!i%kNq7{C}3U9N*xmLKy3Y)F)F)RF|6)v~JjaK-c z74EUZZYy-YXV!n36^^#TtE{lb3hS-#AuIf;75>o*pSQx)j4xc2)hN=nlALT6rs9te zf3w6)*S^;b8@tS~^N<-HpT7E8Gk$ir8LoNg#OaRDmwGQ6VYbI_g^gC&VujOfGi>v0 z(`|LOIkr+8_R-d#88gaur>(&@$~K>`cM;aw?zYw2X7V(PnA>=&SKsH_E+o$UnFai_ z{0+8+lvrz+pTbi6&hCfxfR-q%yOJom~VweR(O#Wj3%(vcf7W zyv_=%t#GOp)>z?eD-2uV0xMi(g^gC&WQEOE_&qCJW`&Pf;bT_#gcUw%g}<@Fr>yW# zR`{$H{>2KHTj5Jq*kXlitgzh*H(KEqD}3DwcUa*&R@iBUA6lVdg`ZgAJ}b1HW5PYp z3iGY7$O`>dSY?H?t+3e&pR&SsD>SUod9GP*i4|5^;Q}js%nDnqu+s|nS>fkaXe+kb zXN9A!&~JrRR#;<&jaK-i6}DU9J}b;W&n!=~!r4~1%nFxVp<#vjqs;VWR+yTHn)O~| zg^gDDm=!L!!fDfPo8B;UhRD;@-?VA9GiKJ#wN0B=H@$&X)f3+kp22%6V%oIu>^YLa zikLQS*7SyO=KDO>Mw^i|0lBZsk`qZ&rBvZF6UyOum^62ibXVk4&$hp}y4!(C)guC*3?5PoP44L2`(}m zpQ2{Q%vsYTbHm=+yYEV6_14b4yCG7q+FB!HJ-z;$^IPR-W}g!_VJ=PRsXtwM=I8Wx zv-#gtij*^_J{$oOGv+kZ&7EF5^Uj%fg}rx9uS@lF=4|gjX^IK=o^(C1^7b^Trsw;& zHmRoe?z)BF1T^V)WJcIqKQkPuze^2MO@(Hw`ObCM1yw8fZjNhuR`h;9altbGQ}fy! zkeR7hTg}~f-ZjT7bEsjC%!-;GAek#Ab5Nyy$A7>1mR>1p?wo$t^xJ37_-8;pKHUj{ zD)sfi^=pvpfqt)aDfk};=gickpRw?+>36a;=A;>edhe{e>%FOYk@`M8JafUEaK`k{ zOhq~A$@8t%rPKXOjgx+V4UYe${9fxlJiC78^cfk;-j>2=-r>W&6mRoh2fpx@&uplz z|3B=#33wF6x;|bLvL|E@Bw?GdZ-D^=geBbx0Sw48gaAQQCd)tqS;j075G5dL+#{mm z9uW~&;(|s*jf#kh8Wk1y5jl$M8TKHE|M#oR&jIqx3=TY`n1*G z){ax%$caD!)Z`iB3PT+V0PmDe*&T@R)WXEg37nKv;; zoTtKFj7^NjeWys~_>47-O^gvEI6h-DqjjY6H|}SR`&SqHpZk83d|l|nW$3qbCz*ut zRE7J_O5DAcz{j}vG~c-@%bYtsVgg|$g&i{hwjl^@1}3sEkk1ga&d2>&d;S`jHnDU7&O zdL#FL9=}>zLG>X@!RP}<3g^zB@@wsb)Q_u>4z0o%YcvI@&9ZZp>D=j2+ygtY(YhB4Q{0OtkX5)szSmPoe{~ z*uv;LR@ozvF@n*`n8Ijb`(`q?Gv+Wl7z-I)jMa=ajCG9lj17#t8QqK?#=vo^d=ZRs zj8?|}j5bF6!eb4UHH|To(axB|=wK{ltY)lZtYY$YZ>bqXH}JoX=SA%8|%Dtv9^<4SS2Qv7l~Q9l>%V}xy$i7M43Wr66jTsCv;2I zk!mQ!E0+PFh%e(&2-2x4XEr6bR7`T_iD|jzBHdLETrQ>{|0z|)BE4#nnB{bd8CY`K$#a%d z036aUBZfT8-jtu0r%w~Y4k0&f+R%c6q042u#0%gCTc^QU>0mh=d5g@D)nANh)0xzm=@FjE4U7GOM$^T&+`+#b?NMYwT-TOUo*) zASFeWm6$i7S5cK;XkA!T?6hK;tDvY{uD2UK(Fk|(jsN6QEXI~sVR9<4F03k*TV-;Y zklS@~sjI4TXjxSyz+3_&odW05qI{WCZb1PS0E7MGRMSR^;G z+L=!)Yb97|GFNCkma$}+u|UX6RZ<`MdjYGTudT6W96`-QWb1+2?~nlK;=u9$oy4%u(~WyuBlZfS6Iyy`!K2s7h!?ZI(g^}S~{*Q zFDs^mu=4=}NIh8(*;dFIR-*PLds&N}3oEVCO4QKa@>MvUR6UV}^~a>LQW#&h-r40L zJHRyK%gdIa;I1N|B&vY|UE^Ggde+ zt#X#i&NN6BQ#A8DsM(4{Ku2UcLMD?bm<6z(IqP zh77e08-8B$h>@dGMvoagE>+Xha`Ui(zHm`t(c&e=C8cGqOUo-NtClXSUcO@fMHgSP z!134Nm#w_~idFyH_5a__|6jI$`uL0q6DOUYIeE$jQ>RUzVV{{bD;w{K=ghlM+5fM@ z|4-Q8!ZUzLlAPWw^hYM5iYNV;0)#$HVwb7TKx z19RHLB3IAc*elw?+}M9;Q%$u0gJ{`H;%p(*P&CF?!i`+iuQHlyT^JwM= zna42qFpp*4!kqTnq{~tEq&-J+fz0C-6%ou6n8z`vPiT>|GN-*ha{ZZOzf3wC^X|-3 znA1!1Y)iS5& zV93=mH?DIwFdwXfMLqK*=3AK4vohown2%IcG%_E>yoq@V^WDrxGjC?DG2h2rXYOX6 z#{3}jbmkuB8O&Rl%Y|0*o2S};5{CyeKc9I7^U2KPm``DDWqtwk{>+W*IvevD9G=45 zxDHHXK8M3InH$%AcIFpycn))Q6^-Wyn6KjSLgp5p09?#{nAb4xz`Tw*z4#{`U$6Qx zujg=o<_*jPm^U#GWZujiUxks*Sl?rg$~=O(jd>(< z<2pQwxp5sH%{-0uW0>2S$1-;?@5J22oIZX{u7-I$^E&1U%`}S%nve8X5PYl1oOa)RQ*RXk7GWHd4J~gfqil*%ttfN zWIl#@4)d|h3z=)otC{P}Yni7p-@rVb`4;9G%o~|cVBXApB6BzMNz6UW&u1hH_kxW5iy9>L*3%&p9WncJ9$GEZY3!Q9R~nz@5{409Lr zPRwhVCor#L-i`Sd=7X6xG9SylnYqT?%{+s-hxrobfeTdqEqp;0$K02Bf93(qQqb=0VJD%!8SyF;8G_XFisBA#;nBWn`UW$PV{RF&^!qap zVxD5?GtV^iZAw4K&}Uv~=rgZ2^pll-t)b6+gQ3rSi=m&Q^c#(E=DUsXaVmVD5zhRe z5uT>PTa0k#f%#nj2`W6!;F*f|H~3V=Qw(lbJd?TaO2u=SConH$u6E4?c9GOdJUx3v z&RB=c3NV`?I-(-EYt0)kv^yv8%a&`_xKXh)G;5o$uNisR94 z8o3H@y;S*%qqt`i_!Bz zq)E@(D2q`l^t=;2gEUJ%|4N+tNs<^ZjQb$!FG*secfXJNNs`DA#(fF(mn7_}8uvle zZ-`H%#NgD^d*^%Cx2fMGiAln^uUaVcpX3dvew2hz?)I=>6?i6g9F?PfMd7o&oSr_K zjQwWis9z;vb=`>7 zKcoH?;!^N*Z~7ImHiaAZpnjT!((*7z?v!4(H$CdNR6Zj;>c3P!o+eNl%5S!*y~tkU z<@3l?1?tz7pJ88m_K(`n$e;Ro5=v*>cT#^RZrG3dJ>_TAkNSTSw2k{(8V6KAwc||v zXNBY!;_p)^nP-p7aErxnI!ur1H(<^3XV?eHo*l(|9Frj0+mK#EtxE{8GEL zw*!r5bN|Vc^=>T}jdOE3X}p^`jr+D-jSr(dNf~9y^Zwp zWj)VFk5Z225pz4L`J^>PHLp;QQ3pOF1# z!KZEiR^z&L9y5=F3^~`T^QP)&t>a(yv+-VgoBNG9o?*9%C_6c2mx_6Gt4^=*PZ@9{58n-UZ+tkm_oe#|b zMh{i_WPAIM%BOWaseC4Q>u*lCZM&ApnV@a?XJ8~Lr_!Bb;$(+8-uh7e$;ap!s$BED z;VOQHcOEh9Y@AmqKD9*K_{JUX@eRid9j5cGuU0@0)eY5SKD~T zjQ3GESTu3?KK5v4{yB3u^QV}5nAbBG54DUts|ACJw)w`99`Lm^)ZMk-3M%^O@(c zeJ#vIg|d%v{TIRfat^mLf1bI6;~V>gDI9K$n`#dCW&KPJH?C`Pm|w%;HLUN)ypY3< z`PtYvH1-v%Iea7Q*D}9_`3C03e7uGEGaTN?{4wSZ-X}1w6L)j?^&DQu}mSgGv&IP*B>Z!z!Bd>!)?=G&QPGJk`44)bT37c&2Zc{TH9 z=C#a?>*Nj0n>c(6^N*OYiupd~pE5tl{B7nf%(pQQtWx%Pm3bWV zz0CVF-^4tH`4h}DnSaAPhxx6{3z@r_S2KT+c`ftzm~UYIE%PnRzhK_T{A=dBnSaK7 zAM;(zT|6FpF+a%RPUgnGrm-*D!r{dnUd!?OG7ns;>{V#Q=k^I^9>?KRnH&3_LzwsH z@CxP)96yA43Wu*}p2_?n<~hutWM0VJIImYTe~!azneQ;t^bI_h25!;knF>=Muu0_vdh4hVwqB5YFK#9DW<~Oy)D0@0+Xgk6@m| z;l};8u`iy);e{No&elR0``*Jiyqd$SnH&4iW0==+_!{Q!xhnri=5}sRo%sgVzn1wH z=Cha^`|QJ+H*)xL<~3~3KFoJ>_#)=j9NwGxJ`Pu_XZU##<~MP81M3fB-ooJ}%mb@c zeXeAl#`Q^M9>?KVGq-X0SmymXd@b{RoS%((3WwjzJd?RvwZn5b%&R!Okoj!p#&aoA z%&R&4cILIrvzc#Teg|{oIhJVVTR8ki=7C(Fk<1%8{0inRbCrE!nD6HBIm|_l3Xf&J zkHhCOKgfIo^A_eeFb`a=;@{6aj`cGvCd;f%!h> zjm!@+e}{Ps^Ea9I=lm0x2d+@{zlgc9uKykLI1Yb}xsCZt%rlv9X6|5qr=idJbzxr3 z;de2wWqvVp59^=Dd;^ECU~W98lE!=shhN7$Zl0=73iCz|uVHTG@X^e7bNFiJDV%>- z=KDDO66Ob)moiUd{cg-#ID7$f!TF6~9(b85-%92=9N)@3j>GeqXL5LV=KVR`!MugT zlbNS*_F&`yoI@oxmuT{H`$d__qn;aUp4bQtnQk50q=Gg_t{R~oicb4R?*G+i+MN22w%dx zCI&CYD!*C37%OmQPHP=zUWU~NGrzQ5IIWVJ!)cY%%#BrUBmd>5`qFBrS`ExMtph3_ z^|u(|Mhdham4wn8>8t%0W4Dg>V`$xh-gj3{tw-e{1$yVYH6682L#qPHsePJ!ubrtZ zNwBhEC$(==fK_hg)H)fZXXdT%y{T~eYYwNc7_{ZadC*t~F5%rKwGXt2R~?M?om{LA zE2q|P^1S6$+}N!&^z*&tSK+PoQM|}oKE)S%+gb6_cImsk{ZfS+yKhE*^xmGie69Ul z>96qiL$w}5z2DHM^%r`Z-OTgRW^H-<@V4>k9d@(6dUuh0RDP}Pt@t8u`zc=N?Kg^- z;N5z2dZpg>Q{l$0qmjPLWKXJ(diSz5f0wu4s`#`jZce|dUHDQ{dbG~&G;!MZpj4Do z`%pz*d#ZgyW7k>juhQ2n4g09|RAYCS!f8FVwf`90n1>XnzeawfU+nFVD!fF#D@qz_ zAEdQ>YM-WcT&jJ`*7riy{*yVJ(x-8&oLbj5cC~4rnCfrro)f3>n(OVqY9G+p%~t#C zG%pzTQu`dO<5h8E_g(G3Ct=0fn77sb8GXw^Ikn%l#5+FJzEW%XjeQ@(hjwKX0@mkBH_L+^R8tA)F z)Zg>G_EOw<+Ckuar*?vj8N=Ylh&Q+~BGo>%@sxymPC|VpN7Ya5^R<>o?c=nLAH`ei zqd5IF;?w@8@wA1puS{QFF~ZgUfsq2O=hHdKNWs`2F^AKBi8-7sYLv*>PiW<|k4|3& zG4eCU4~=SimO+K6{f)EFhswVKc0ZTC@;9G9)qb9lJJpx^t5I6wwBJkLMKQ`lWgrc8 z=2rW(t@c)5+%a?7_cxwWq31ZL_O1O#JqJL4l~eo1#?vqA`IRK}5o7*P&k4A^=Wo?} z)L*lG%>L&3s(t;XD1~xr|Gl+5YQKQy6E(xMay5bsZp2r-&|4nG=?hNEDPG~79~7_j zogBM4KJ6;wcOk!5r6&&FpkXF&{kjk){Y-)$0NLvmHq!* zWq5p_O(odo)!(~oJIZ&hm)}40P(ZKAd0y`M^WxpfHRHUT(xEhs^vGO35ThGpV8@X5 zQfFhQ&{%_F-Maf;nosGt_HJE5=(z2LO2Yc3>o1cyEqOJeJ@D?egiV*8xrVT&?vm>X zJ&RwwiLn04;9CgY+qSJEw9i0sRxSg>1^(XEi^lZwylhAtd*j$lfDK-lCnd!xifi4PLiMgA(W#?rWnxI1OdLxd48T(Fri?aG*k z37a;2DY5zY2e(N6=;B8Rn;)?~N{Dhu6nAZZjCkFRS3FJ_@p1YSg!PyDK1t{**d?*y z$!oR}Z|dJ$w!f$ET8VWZekaj3IO8b_PfOk?(ebl*ns`Lk0*UUzmn7P4U7jI-_m*mj zwvRrRSig1nv*h2b-7L|%`e%u@W#`NO;YfT?Vp`1^h3n@%NBW+NpOx6~RoL^yMNE-I zhvjvNw#{7|rGJN|5^ZtsOHBK|-wWhlS5hOf{>gn3VV~{fU%#MMqO18Ei4CVmzDWMf z2X2&TyY_&@w4cW8Ab(fjEfOORd@r&7@^LSbf6d3YNEE+(FR}jBu`iQErNo-jO%fZddnDF9cSIuE@pX!4 zy=lC{<|2tT$Ll0IER7P~p06d^-tgNg>v6?kiOpSSDPF%^VNkt9k+Vyp?S@|@rfrUr z^OJ3JibVSjmq@J3St~Iuk@WRe)fI3#O6?^#G3xsDIBy_qC0l4 z3jd)+qU(WfZ%TWNA1^U&&q9gysn<)i-LX|-#AhE#w0CHcXzkQ>H^r-qOq1AjEMKDg zwQD4b<&Q~3|C3l>e^{a`s*{|L8%jn?taAu=1X++T`AE$?jD6{J0#W& z|59Ree6mT((W3{n?!o9S1&A z;i2D2Y#MY}Vnl}J1Ij-wD?;JC9ujRiLnMkBsS=x0r%J5vJzt`$!(xf{&#NV-J#vji zhwFBU^+O+$i1|Tc)9tTGY|uWC==u5^iLPb8NpuAMA+i3JkPoRm_UHtOt{eMHj1Z$F z+Lla|*!~R}}tK>f8^n zIN0&F_fm^W^2Sa$@=@wsZRA5gU3fG#V_89%1R+Q$EVU#-xdSUzZTv~Qrc^P}III^W@^`ApvX#Ih+*q=wl>dGw`W+Uv*t z&;K;<+tipH(|YW&_-n8H?f6Xhk)Kk3K9bNqWJQqnrcaaW+6S9bXTFJ@nIREcSn0dJ ziTSbG)@>bJ1FrriwdBE(&%7`(B(!wb77)(X zvH229;Ge0*i*~)6`rH6*@MAl74E7nMO>lm7d2{EJsrQZQFxQb5t2w%?SbzNoq1qLX ztnBA}qOJA4O6mC{urC6H4Ym#FRk-XZA3~z zpX=Z6qSgF*eYY(OdT0ke8*p;kl-}C<(4PCO8=|zJ?|g5~3>m1Mac+KW&a@s{{>+i_ zb)kvc$bR$AREM{uzTI_qpW3VlZE8wzs&2XJ*XFcKJ@x-+Ne=u{`jh~x8@u5WbPGfqP6?Of1G^T4TH6A z^X`cC{4_utdD*bi&5y=wv;E71=9CZ7vL9b@`n;}vwXBV~?+;oyK(qDR8M!+nP8<2o zg&{8%ShY=-errRMgSAhV-@I-1O$pjv_g->OV$zSPi~m^F@%wyTi^-q)S=ik}w9pZG z@pZihXsa)&PN^*$u5I1?*wn@I$7)@d-t^_-o}ScS6UGetbjmob^NkfN4;I8~aSN|I z^vliXX$zMwJ9gi?RIRc8M$g#H(OTb}M}G_ZYOJ<>l+V1cc8=EGeEVnjk4F-;$2<00 zGwZz(+V&R)&Z?X-TFbt=_O^b>0h;H6??S|dq1=xBw3ZuweYTJ2qt$ezM zLx=QF)ZTu0-g!q{9ko7bTcWR6HAw4zZQ|>tw(~T5)%A4x)C#(Ndc%kYO{-jgRnOf^ z$7tVO{dLx^uvBep&eiTcn}%xl1vh=TaqU>`gZZx=Zg|M5-FbX|Psb;z+9=Q6SA{U(IY z7BuIkdBOvI{n7o@F4Fk1>^S`RRzPn9ZpZ(%5Wmf!h#hw}E*FWn?9k4$)q{o%I zwsl9q&N~uXQXhT(ovy|GHSN~mo|8lJ`)d~s-uP;4$e*c`g1Y`NF?)n|Rf&I#?fbFX z=;F^j-`v?pTitN;Q&;U7sqNmSATrqWYLE;Dci?rn=c>m&UqsTYd$~y{mzuNiQ4yp)_dRDGgh;I z^wI=Bk)nOO*wOd3TLx+040^8bH!B8eZ4 zH6LBE@N{a~H;dzspEpWda;0`8;`yOkbVR}S(%R8l!VO!S3um25y>!5hU5aiPfpV1e zbgn;>dWX>cugE!1i@QI{=ZCnFJdQ?cQyhV7HwWliV)pt2uOyGtLTu?_o~Kf@{4SrY z9iO3VBP&N=5|lqi`#S!GIaiNQ);53kv}N7fBekx#4ji*4rJrV7*Ey^-!B^Fzt(u+RhRF0}nlPC^dQ9;9Af4N!lgnAAk7n z-v(+298<1bbwOWk$nuQ+<2w$}EHk15t^KBI^WXoZ(~#;ZTIRyfmtSO`pe4RL?Wetu zOx1RLyfQEK@&Q`(qdQJs^psWmYU%s=*7?J<%zlp^`ToiQ+WH^=JX$bS*TOG6xN`f; zleEUo)Bh;CXRP+vrUh3%w0MyA!LQ-VU*D6Vxo4#9oZ2)%yLVsJiVG^zw5U$*oxT&g zXzMfId(HVqn)dF9&(dSwJWrcGcFF8@o61u&zD(~B^t)9%`q_6uuA7Hw6R-2V{&<%Q zv^kIb{!P|zn)Z_OfCySLk>`hLnq&W{t2dTrYMzauhqgK=X_dcZZv7*2sP^Uuo38Hi z@>I>abjz9f4Slq3Qxbw-doNwP|L(Nl1@ES7E_>hIb4E_o96kGw*muP!?ZX$+Y&$yl z)(-!D`DK5-khe*JsSG(t-e#W(9I9M_sNx0wC!`>=;->yrUk!rFsk^i z30iEg?{1ABoT`mqvmiS0&7Rujm7h=3?&_xvdN{r4{+83J!vYWaJUls3`=V;~$_t*D zt_A;)f5uicT^st_XD|DIo35?vdT7m{!X$0wrY~v_1&q;-m%o~nd@@b@tMwVHy9fUL zse1xWTUIswsYm^8d9>>Tf9lqj)je8P|EX{LqWIRQMl*M#GJ{?r3| ztn|DZ@Tcx`)5MlHe>|f%{bG40V9y!-XUp~#HBX+=7d`KJY}hSl^aJ)g9!jV@qgVdT z^46HSXY`7MFPGmk=8WE5I-uKv|7Y~;u5S76lfV6;zum3=OUv7T=wGaUVX5QEKlJNX zb?E(?)QgY*;IP-w$MNHU9U{I zsPyn@J>>2V*Us5@TL0l4%k3pEpVn{r_7cBmHl5bTUw7)lzSp1DyS{lT{-4&bp4{<+ z^*N_?Ps{6;n|1it4;j6!?`hrLw7Nq;*lGRx8?XND%44VW6aMS39rV>H{W7iA`Nz&v zdh_;IV@^JPO7Hb&+@S8aozkyA@AK5+%TMWkzrFn3e&;EDMwf%nuAY8Mf6ss4l+015 z^v`OF(?(iP>6?0fHtd3+Q+ofc5A3-5@bCI1b?a9h{Ni`Li|dU9*BigIDCK3;zGEe|BEPZ8!aLQco&e()s>RPwGAo9GL#WYbW*3BWC~9`^l5KS|2;9 z|Jig?$Dvo9)XN(~TMoER>Q65FF670FPU`L-KRMlyc~bv=@ZGC+C7;v>>~EfY%6d}& zxXuzaqvJ{a_qcm*e)VLFu8%sjBJ2AW{i8VBjh2sE^f78y|d$RC&%IA`kSLNmYn$Z zxSqRvb>Gb&9M?M+zuKwz)#G|jjA+^LCFvu7BNhUE`edj_YZ8mSw;9Ic#qax^e@y>5{Agy-=wtc?ep}}JIq;Ys+p_9T`;O|RTl-wo`1Vo#+nYCkrM+}ipZ`i$ z{U=+G>QgLBpIEi=sJ^KA>F-A0c2uv)wDk0=J*r<8^W^#8t~jbMbj(YBujHtH*Zarb ze8+KAe|zbh`#+m?R6q4up0j1rQGMEPkJdNDkLbIq9=+wWkB{g-Ja*Zp+uk~&&mPe0 zGsjCu^q8?vMUQ>zh~9Jeij>|D9ntew#SQLy=Mnv;z=a?6yb<9ktD{p^AJKoC_|^yW ztB&ZI0bd`vvFM22GVuNd?_GREf3vn=k2UiX2`65sKN?(o?;aJS`%UO4oDm)0IXtY7fKnFpr- zd|1Du=7Od^?!$V$J;@UMF>vteX{X*gtVfQz@TQY5AJ)?bS)TBH_OR~v>XCI_A33bw z7jhkP67m*09=k8Y@qYN$P|PkZlyl;1ButdHooy3t;7Snp@~WaTr3hxJ42 zYFs@pIjldu?3OF;opo4W*LyPlAJ*qPI^XfUc3A&r$6eKPk`LoiS4(S7aos>4^#Y6gc+Yc{V`P3o(_RA7-UwHVC zeqW?J-g4g|{hp0C)XZ9cNdIC`-IWd39nz!XR!7FIKBTYxq)YsD%MR(wE5={zUwTNl z3_oqZvEY#2v0v4K?(+}nZ+-m7gD=cHq>p_!IBY@YA-(tc$v1}ShxEwZBk=!_{&>@` z*IqyPkba)+;vaH)9n$+|J{)gLK)7^=^qL6E%clasJ!c|3XFU4GKF|FA>oJdBchC1r zj{V}%BOPmI`ycS=w@+&61F?@?wrH45dCI?P|3c4TeBE_iJwFz4LgBC>wjuPa?(EVEmovX; zA-*`PK7NYtu@+!65#KA7-$KRLXe;r-UhfA2fZY4fATjs1N?MJ_lR*7!~=NCHhrPM0< zC=|+9<-!Mb>3hJg+;V(b75O23yK!tSs<7g7z}B*b*1R%&n4L0*^M+Sgi}ME+yR4d3 zKI%r_-*uJYL%aAUFa5d{{W>81THPXiGk77s%!}IN_s^*Os91HSwW6@B3SaxpbIKp= z!bfHCnM6uIoqjA-ey)~lK1O|@RejpG#92~?kF&$!m#!*{N|j<>)xw4N5-&2O&&pPm z(2o+%D$lo8;MYOPw+Ol8ryJx?3)-tS=o`ze-V+ROp$;fHe0_IOIX;YREzB(@s}@wz$7-#raQ!O=;Iqrc z#Y0=aG)uL>_t|stV`P;@C5QlP53y$B5C^cG*Fc52r3JExPJB<KccD~RIg3zAY8Av4WM-o@gDuMKZA%Crx z=GCKj8~$3a#;X_l5A}pB3FX@nLi>H^E}zUwkY3n7)*}_K9+AIDPwoyI>HPyeX+T5I z@2}IdBFLNXU+JY`bUd|u)P1D;UvsMc$VkQ(u0M{g5N<*ok+aIb)EQI<0y&U`o%}ZXXCvb8TBa6@|)2{g9klkNnxmL;6GICpR3D2BGv! zdekJ6Q$M6I4dUSA_Ijjj^ur5rO#Q!Idb0Qw?;rKAWpDp{)l-Y+7>~t6yRAzzsQ19| zQ0VnO`=E{D*}+~P{F1VcwY3D-!iR<`J zT-d+p!v965&j0^=f7SKY|K8uIe{&c8Z|>Cn;(z}ByILUjKknb!0-gTj{$qjvSm58* z0-gWk{$qjvSm58*0_Md7xl({Xw7x-W0dVr}KlRC{UyFkuNeqM>FAvDo0OGuC;N@>% zt%KGX#H-J3zU!82ue`jvqHM{cg4|2y&zqB-WuHEEO6K_!GsdUsspH0uP8l_FMDp-q zwxL6k1`irIa6tclef#w8-K%F}qP0i&?%ld}?UIlXA0OAbbEnwYn3(A3sHn)uh=}m; zu&|CDLqkJCf`fyCP=J5{e}6weKVRPt9Xj~<_*keOa25*)yl9yN>l|{8BPb|1I3y%A z6sr?qVd40dpU9}F=;)Z3Sp3va=Q#Y>4}R?jKlekw_d`GUqki$n^pijG7k}`BKYH5u zjER#nr%au0&&r-NZ~i5@1&fxHRV=%F&9%4O_0YB^aSg5eV67z|Hl#J68L$c26Tc@s zwQNxleiWEjq|3@Dm#W{(lRwqA1grnf;`3+;ORT75l4sP`k z2k%uYSL+R>|6z=Ct99k}$+~(f|7yne;j%Nq^ZbnLsrX?oVLfUQLudz}cZqzJ$m;7O ztOz&!dfRLw4Sx0bV2Rgn>V?zNCrr$kVb2!sSRYY`s;V%oYFmATi#UD)wlcT3e61g4 zqO8Oan=OAK_8e-!X_0P7Mpkz3oQ!lFirTh*w6rJc+W1YDdK=(pMVN{^IfL{X+v>@< zO>O+_GVX5p$#Tei?Ku>8Ut7H#QgpZVlj$97NyNXzRd_T++>JDxWZ1Q&qk%%8V=~CH61ZF3Q8npeF_$bGE&Z8~IsyXyTJ3X2H zaHLQ9WcfG}clnE5mA>L7*uyW|Pq=%aoK_)@$~4i5+xb~SJBZMYfgXQs5+sfZ6=M$g zi@tjUM8eKM5ndT2f?->~>@bWc?5FfX{hy`!WAz?-0(t^^0(t^^qMe@QqEKfWG2*r5uuVH1%a~v> z=4h}Of&MWBX$B7Wb#<`A*0TS}GO4r+{DlW2F9##dv5oA5w9fKF|1;@uyQ%mnXU!Xp^FvW{R{PCr7qzJJZa+Kqmxw1WMjyfg4P6^0A zVR$FEVPE?gAufj~J1M)g^~0XESx1%^HW({J7i^NYv4Po-^_rpMo7z^->?iFD`^oVg z8z^EcA+Sd*${9O6lE;6b3{S+5(~L%#U$(z+kH@tX&L-~Xs0}b);4j+yQyGw+V}cM% zA-@=79BoYeGDz3wxeXAP_}vhj;fFMdKM9#YewYi|^EPo1-X>jebKX2|Z5I5g-6-sXWRP(Hzzk>I$)0x`v9bnDe?~&g+V?)-@~6)5#sv9Mu%zvUCa-ow6fD zr{r)I$1UTi@rO3B&A@LFLu6k?eau{qU(6loSK-5XE=HU>=sylw*{E!0=9XX|)t1yY z(2cO;JOOE7-8P(cY7l;-Nyp6P+&(w3un2Bp_@-;<&96q+hxIhtyJ{7$a{Sx!OUv{|g%*VGvaP@58 z&i36KAo+9fyNLW?UkW#Ka~wH$VeA_1WnF;ZJcg)w1ec>G&hw@tk>+5uBlQ=$*ocRG z$$7Qma=hc5k&F2gV$A(*IO(A6ta(B#fOzvYadln^!+E76&MToJW*x>!U3f)u{U_DBwt{CW6Q8h#kB#CJe$AV1isJ*PChaS(1!*U~*y zbjNwMJIfR2EUfDT;`m~~WN3qfBC z7UAVQFI4)Av=Yp@5Tjk%av!ACR>yL2khu5&?g0w?aJ}X$B9c3Z2p4WheJnzbAKBhM zqPbj%(~zTFUo*F41d0r#8;k%V$}UK8z! zYz}Yg=n8d&V7{?*NB>EV6y39A-)W9%igHCdBJ8pcRdRXcxdP{i>=2wQf<@QirgKGH zu!uuj$Dt3#p%2ClkMTsgF*c|js4mnlXrIJBfuhd=KhdqgS43uaKp5^v>+t*yCB_7cm;<;z-y1GM3p(RkITqJAF{1evd`khcljEDYrBjgTROu%= zA-_(@uM_g?f)V#B@ors6BA%w}#G5F!R7PZa|!<3(F*BJCc5qKD<@?y@yq9UY zU$J!WPu&A|9_wDy%_2^9w}|yf*OD6`atk_$aVWEYwx94v+2q`Kz*pG%S;U=?CeGi? zEit|##u6!zx-F6BhoiV(qH()7OoZ+X7lD-#!Z$lc)b+QBy^t18!^|y_J|Z%!V{_u5 zFfr(;5Zw=0MCYA8A{zZO9JUNWd3@2%&4UpiG7dM!i2`FEx&pcax^2VoQm{!^^|$b| z`&&26GnODrCr^w!syU)5jBTW@QzFs#BG5MBqHDRS??vJ~**acriGBP$!i=nhO+}i#8`{4Le^8dbnd7O#F)T22!6vM z_i%pgxjCLZZ>~Y#qHF)xjQ-mV{Wt2Y{%hC2&5Z;G@>*b=RQ!g?S|#O;k15h!n9 zL4t6kS;SY6nAcQY+j3lELSH~%Kwq@im*XAlI#VrTF+`1N*av;Mtslf(>Gld0y^i{b&IfSL+UYAoF+T<3oa>AJKst*cYh{`=o?tuZ2cYF)Ght@E2$K5h%5it%igp-!enl3s6%yUaM!Kjo0k+5raRr&ywv0L)Tw@V_S6W0a>OjcX z!1nm~U=fe|)_9aJ9yX7ME#rsBdMF-Qc}cBByaic>c!Aei#0(sV0q+7Qr`n+P`rpWJ z#&s6)8RQw(vrs;j$OrcZ@#w3Zk6W(2C1ZSK;XLgQbkQ}FCEVBBrWSc^5Gvv@-^Rh# zaanwA5alDH%0ujy5DV^oV4rTGqFW`MmvIijbwoGVqTBFzPiJ>*Q=}<9qkO||wTQ8h zD>`#My5gDyW3#>e4gD3+eQ&)*?BV(Z(OMa;9JUREZ9BrYp(1`=tUFSr=`HW_28*~EGQvnR25Ws71IC)4nu~%l7ty*a){n9x zJw{zOJZ%wwgBazBq;qybsOXX$BDxIk?1^@hB;VxI#d4jGy3$bnDh;R9_+cTU zNA~SLJ(Ab^^swCIBd--{wZ@@z9MCyepSta3i})DQAx@143ylY|>CS zhB4U6R#Ul{jn6O=B;ucTl^rbIaVHpt&WHUi$~C`Ds68rIfBW|a!Ey4=<*(i4)&cS(xZrk(cZ0I#WynR8QXCpBuM~LtbLtODm9cTBz z^&4HEcEZ<7zVLk_i-x?Z(e;&AVeIX@2P7Tnu85Pw9Q_~)G+AIXUL z9)6;urB?X6ThkS|oJqtO>3{S}Vcok{#8qB}>x!#IVDdGh<|m8z0SR)DPAVLcLM8IqC zqiYVdheRJ>mq5`4=gTfQUv|NDLzm$(_8{2~e4fWzo6)wz&sfB0NH>gK!gHPfJ7WB# zLF~{Wl-C{92gW0>AjF}(<(!UtAF5ZNuLvy1UIo_cP-ImuT)I;{!yV`SZcj7rH{m&f zB@=s&Iq&<3LioD}!LvX1Ee2yv9YXsJR^%1YPY4%o1Khpg*B9Z#!I4u zYY|A3*3M~tuO4NnkB2UHG942UmJd$4*kf^{Y)udk#E$eGBajy2T9LQNRqJT=2y1}2 zF{hHy{s`?Od62dPI#$H7BisS8j)Fh@{)A0pKkyNQAQK=LL&_klAvZzlAx}Y?ARj?~ zgtS0{KJ>wTppO^|(I7J*xsWQzTF4!cEs&QW??Bv;Gmvf{`G^saIgraBb&xHPS0Nuj zzJ~k;IRy#X>mzzVMna}R3Luw3ZiPGs*#UVM;)a}nM1SlfdO>WEG{`)N6H*CT16dDw z9P%3EYsgVZ&?l$|BngrVp&sQUItbilVFe&S1mbO?U=f1dLG1I1aO~4Z3d}GfM#PFv zqBEX#i5Cf?i|8u4iSD9@u;MPHr|5-mNcItZML%@40eBB-kQj{4Geiu@EqzKa8`^q?=i}hl@GtdsC>#wT=eQ<8!}$L^(o(Zgl6KA|YT63<1s%zoL0Xy}3r^h(v4NL3{ZP5I`np-riuL!0wCOB;DnO(vC> zm6-FSGT>3H-16n)<@?1(>hewP40@TH#s*3uJ*MLg>NXxI8olt$I#ZoXoyFb(t{NUu zr{h7W4DN=`0#iOkrA0>VWD>-^Mw7&3Q4?RVtf;ckTZ^I!SvHk|_bp*bOLD6-WT6a} zS+uCo3ArMio7$<7~CD zOlGOGyaFblUR9EZm(j^|>|-+KGQ2R&C6F4#jUI$qo}MQ%+|4r2jh4dd;Mk1{qg?qU3# z@gU#p1jCV0U%J>|kY<&F5^P5%dbh_~QgMuQOR_5fE7vzXT`#Ds|-6k;Gqsvow-Y$XQgHDFd0)wQEoA2y7qz7&0*~e zDEuuG@P!1N(%Kd8MUD3uA|`et&0h%x!9a|Ara zSFO3yB$Zx4g{32rZL-a*sYMl)nK%iFCw;Rji!*Xvl~v`)9gSCADYlW{)UvWARjx@@ zrLqaptn{^t9TYOBsJyZ&w|I8xGS~yZ&(UFW#RMn5>aoaKZb!vXNU_dub}4`CVPbW@ z(?$IR1(6@-$nu-zto+O1H)Q!MbCHFM%r;lJkUp6WwJs~4gv&0J)X1lVw9R&(E5wUb zMpd|p)glMAMz-{Y5of_9d3L9|i+C!9sdZsq0ird>lrXEhdZTpUxE7N3~3YTBDv%G}9KmxN$GYjz= zmx4OaTrrW-ko{=6%P9(5^C3UFTtIK%+hJN&ab?l?<(1CtvbjYC&Wys`a`Ba)ahP4^ z4Hx4&pzFD$aYTwdRtCMZ!_fhM_0kq9lFdji{_&;{MwWp$CK!@EfjxZHEL6ZK8JQJ!bMtv z(~eKSh#RRaGbT=>6km`j+U2NzPo-W`f?+?ks1&Anzn#xqF@)kyEL~buURGLy;h`#6 zff_z2+w)vOk5C+L53&}X)lk-;Yq4y!17dHm$PP`VRu^0q_`(BOhJ5U0Mbz;z1cXOf zhix)fq|5rt3n4MVH!HWO0=4E*YRvNg!T+uW=sQXj#{!`xT>XDjo7Cee8V z#Ne3r+>H0&%`MIHvv9@6W?C3UmxK-a8}Q2`Pp@VHEZ-Yj8~14YD4`w6A$Fgvw9*qRX&< zO7@{W&T|~9E*hcn&Kcpr6zr`c9m3I&y*MVk2yy_&=J(9#{WG7@s3ZId10WI4 z!4AMlW3YaUWAl4t^v>DKW6>_~C)@{_ietj>APyXx-!Y?i&7Oj|;7|AtWG#-(?~>7b zX629#@FyIsp>N@sa1tNW{&VLvwgd1nNE37#fa&RIQykO2^c4`Y742hB8?X9?9k_>& zX|LOO|BP_j1lSXC3Fkq4pND;bOUWO0CZu=8=zTNuJ7ebe$>=>Z--&2r=n%$2ig8TX z7qSw^gwr55;FxeWtaYF z{0S=|dvI)ir;Fb8`UUbO{0V=BcyMffPs{uc7ro0h4bt@m*aUbB#D-(Sb&xb1*8`u- zQe)8kPL}z7EqZUuiqlIm^a*W{8*ofG3UUvQ&F^H@Rm!I9gJr+8sKk!?`r%4A%1`~!k_RkWDkyoqxIdZ zcpSSS2jEY55OSJya?$@GlqNk}u?0f&B;i+(&=-9q(lZuT981hoV}zbJSpcDTvIrlA z*q}q`n~!qV@M5AU(T6oubO4JL-=J%oKy{M&2aXx}S;pGqq zj?M2l(YsHZmSOC`pYR1pJ&v1zNy}j$9Gl-`qW7ClK=#3(Fzhmv8OMZ;kkb?wc;*U} z?PVWYycSDpFu&v21*}=kX|6#ZgHZZz;KNs{W5-${euPYbz6W^2HMkDNaUJl(S~V8j zz@^uzu~q|IdK2aW=rjQxH)FoTF`-W#u0vp}2;f~1I!Bn_WukYS-n<2E2Yte~AkX61 z{Jzrv*WURDxl!I{eC5(clhUc0x{YW&6G2^0OiV0_TNHW@YU6+)&P0>JsVF9q!Dr<5 zY%4z7ksH%01%eWsL1}O-5@S}0Qp8q`DJ-W%0Ig6w_Juz zdFW!7v3iF0@#w?y93$l(z28s%5Ou^m(Fnghhe)|akF4RHxGwi`*h6dZ%kzMgJ5*ci zGB!{y{ziazWbxvK=pa4-Z@P?J7<>#qce%?rrt)>X`%G@nB7S*}k8*u3xq`eT%Eb?& zm0zI_JeR{GejRO~Tzm#a@#59*a2avD_i@*F{myyPcE71|vhv)Kb#8*(Aaxp@_OcyV1LMQOcb7Yq1&nT~^ zhR!2zMSKLU!i&KO*B!q+KSp^omtXHP!jy~3WtmqUs5}?t$%yB2VZ=3m$UDZAi;to% zUVIXr#P{KqH#qAg1oN9+#%apiaK{Iy>jYjB<2_oQ#{%%iEtKP9@FAp_U7q)%Jel&% zF7oD9EU}MYt=u;F_h=R`o}QL3&x28ZOciaUj(EjZ))hVke=z4-dgkwMa~Zx{x$eZQ z_?|dxg*qGIH;{$z!{giOC%y+){jqbbcmqzT`uD{k;>P>{R!rl@&oWOq%rp3n?CILRq%VenOo|37F@==Kf>JN^*-&p z_Oq7o8{j^qHk0t+)Qd-_-h|z$7yDB$o<$mq=TB+lqr?tvcHt?M!H=Q)0BaKOfxm)| z;C=8yWa9ns8l*mi;UT0pvoLa-GZyh4q&gY+;M5<3XOZf^3@`sP$FGAg|2fCACoeJO ztlJ2*re2&(z4)PzIp>POr;)Dp>c4OqHzSLFn$UZ@%XkVJ4p4n!aI=4MQiHCuYH_8KSs>Lx8CK9B?x;+$DV+pPp~)AzW52Gu^)sJ zq&k65vR@)U?TFj%cG`@?V`yoez)hcW#u$Yw@1Z}`7xyBynS?K7STkw|ZvM2>zPRZx zxz?0N;eF@;{s?>sXNP~oeaFi=dqo|cRE5vaDCD7A$Vx&v(P9xZHh^x*b#3n6E8j9k%vzqt-U^c zi|w?t3jPsV>L>gR@>2g^ctM3ZHJL-o*T8wC@ruV1|6 z06IZCCLAHvKkp0V;34@q{0Y+h7!NYX=nVD6H~baP29M+68;?1CJ0E@wsr@_P!H1Y% z>WH8IBKs=-UfAw9UcBf_tPjdpL(gAx@4$;c{4(>2KLhtY;*2o~pG7ArKMg;A>nXyy!npEaAl$k;XNFfyd}S<>ECx=Ng6Kag?RJ2cwTq z_aAuWlUx_dXWz+Cr;6Ebu?IfHa)j985^aZJ0`~lKh8pFT|XYAruJcUxUBW^tDv?KoNKXIR+d?maZsm(Ba z5^1e2!r%F4=8ZZ*_$V6T#ZBL#4n7JGBh7yn{^)x=C((y9@RR?-9*sW;pF?W@H2m=? zt~)=6nZT{zcls=T32Ck#hL8R$edSnj?}+)qr{D$8I(1gV+t6wDz~%Wy%0v3-ztK19 ziwDrlIu@SGZxRnb&ocw%;xW|4iywc%8G9OD{389Q{Q!Int-|-aBh?q5N9(9FhL!(t#!`phn(*93`4C24V!U`UA>qYeILmzDeK3HM zDu;)W&Xt8nf5Nq<+=SmiI&U97kCyfsIRBrlZR&`3Bb6V7_aoIeUU3=kMGsR)d4cyM zm7DM-wDeiqSV49O{iV)27)MKc5qyUWPx&l-AJVZA_#8S#`Du8=3f2gIGkgjeF`iG9zpmW$AzoDexx9ESI>Ym@s9fz;l#9y0Rvl5f*YcwB ztK~)ICd-S@PQ9ocTa}B-uay^-M<_2Uk5yh&j;g$3L5a2n78d4q+&sVEM*}+- z*3azSz4OWi{)FO=Een_K*nZ3IT?@Om?YWdBvMaYN?0EaW%Vzj1i#xY(n_JlP0Vj>c zIU7y+e6xFY?_JoVS6`>CUGzV+cKJn1$8qGs+%0=~OLzbDdrEfCeRwZtncEuKy?r0= ztlm1e@ajJ<|L1onIaKeEFXj38=9Pl9erC(UwLACi;@3;h`1Wr9{af^c`1+Y`Tjm$$ zW_)X3ZD)4ve}9(QwLjIT*|o0@j>Biyo|`wm`Pqx{THpJ)$5EchC-d37nYZ)pd@tY6 z5AyDUr{FF43Nb5gC9I^CveH(@%37vnTWzajb*-M&w+7bG8d+m&Vi`qu(NpvmeMNsU zPz)AB#c(lFj22_Xcrj5-7E{G^F;mPI&7xgw7dypnu~+OD2gPA=R2&y4MWf^{c}m`r zujDTUO2Ja76fQ+d(Ne4wFC|LJQmT|LWlGtSS+Yy*Qm51{^-BHHpfoIvO5@U`WR%@y zPuW}cmHp*FIam&r!{tahT8@?DWd#b7C4fZ_b|!*e~nL2j5E z>>S3DJO#aHoH0+nDTR0&rim1rea ziB}SpWF=KeS2C4s#jMzscBND4R(h3wWl$MbMwND>)95yOjecX$7&b23O&{$`*VY=)ZQW~3Qy#+vbFqM2-_n(1bynQfX)yV-7bn%!ov*>4V-!{(?tZcds; z%iUt0Lp(ErcD|GEvYNc~E=bR!^eRb@vh=1yPX_d2QgK(kRev>D4OgSpcr{r~SF=^S z+Nt)cgX*X{sk&?4n!gsTg=^7Tyq2t`YuTDz>(qL+L2Xo<)ZBG%-CqyZ!}VxAUQgE3 z^=#d)cj~?RpgyWk>h6ZO;co;R;YPF(ZzLP(Mz&!y_8#LNF=jX8^)uEmy#aqc%x|MC&txl`g8ni~OiN+h@Sq`{Oujw~~X4s6HaWlyZ z%d)z(wg#-LiRoTiRY6u%l+~1EC1qJf9ahkQ)icSt^WMBaAIyjI(R`eEOcRkdbJ!yq zNBId6=`HvR!9utYEyN4SLb{MG*o97^R~QsVg-O9}c`d&cw8B=Dm`xI|Sz@(AoDPW5 ziRC6b{X}M%sEiYlX`<045_?49hzN8OeSRV@Ow`4RxHQpb6KOr7Y(#{)i7r2p6;>?8 z_!$wglqjSMnSxnp7rKRhVOSU!49jErtbi4=B36u@BgL*^vSW1FEr#qA2D^lh9U{c; z5MyUZu`8JD2wirAAv=M=F5qJa2oe1;B0ojcn?!tk22t)K!b3!NjL1$AUna5D zC9a0VltDcCh@}v56eETdK_=1DC31#DjX}ith?WqM5+h1dM2Jarbcu{%Wn3|;o~o}J zsD`SMYOI>5rmC5$S#4Ll)qZtY9aoK-r{=2#YN1-B7ON#{samFH*4njhtzR3~#x)2m@7IU*aouQm8ooxL5o$ylu|}eiYGfK_qfIRKiNi55 z=pp_B#9oBBOAvDz;;l`r^@+1FG3IIcT7g!m6=}s8dtJ~_ghOM#IV`7C7 z0*2`^eP+N6nGrL#wC82m@7iXUy>7@pXXHFNAG=(L9WKW1mSSf!a~zl9a}CfA8>y%r z=X;xT^wj>4|9b!t)^CTkJIQ%jvoY3eV(I=dvR-RPJGY;ur#qVVD;jzI>;H5H{tFQt Bv}gbT diff --git a/xtoolResource.rc b/xtoolResource.rc index 88aceb4..c3628fe 100644 --- a/xtoolResource.rc +++ b/xtoolResource.rc @@ -1,2 +1 @@ -xdelta3_dll RCDATA "resources\\Win64\\xdelta3_dll.dll" -fast_lzma2 RCDATA "resources\\Win64\\fast-lzma2.dll" +XTOOL RCDATA "cpp\\xtool\\x64\\Release\\xtool.dll"