From 35e2a86b116be5250aaa73fe2eb064ce451f2e23 Mon Sep 17 00:00:00 2001 From: Derick Payne Date: Sat, 13 Jan 2018 14:05:20 +0200 Subject: [PATCH] Build 828 Released! - Removed normal installation. - Executables not compressed with UPX. - Added important links to the Readme.md file. --- .../notepad3_setup.iss} | 5 +- Build/Changes.txt | 60 + Build/notepad3_setup.iss | 1047 +++++++++-------- Build/notepad3_setup_replace.iss | 529 --------- Build/notepad3_setup_replace.iss.bak | 549 --------- Readme.md | 6 + Versions/build.txt | 2 +- res/Notepad3.exe.manifest.conf | 2 +- src/VersionEx.h | 4 +- themes/professional/Toolbar.bmp | Bin 0 -> 102454 bytes 10 files changed, 600 insertions(+), 1604 deletions(-) rename Build/{notepad3_setup.iss.bak => Backup/notepad3_setup.iss} (98%) delete mode 100644 Build/notepad3_setup_replace.iss delete mode 100644 Build/notepad3_setup_replace.iss.bak create mode 100644 themes/professional/Toolbar.bmp diff --git a/Build/notepad3_setup.iss.bak b/Build/Backup/notepad3_setup.iss similarity index 98% rename from Build/notepad3_setup.iss.bak rename to Build/Backup/notepad3_setup.iss index dfd6fbe3c..8c0871705 100644 --- a/Build/notepad3_setup.iss.bak +++ b/Build/Backup/notepad3_setup.iss @@ -108,8 +108,6 @@ en.tsk_Other =Other tasks: en.tsk_ResetSettings =Reset {#app_name}'s settings en.tsk_RemoveDefault =Restore Windows notepad en.tsk_StartMenuIcon =Create a Start Menu shortcut -en.tsk_LaunchWelcomePage =Visit Rizonesoft for more downloads - [Tasks] @@ -152,7 +150,6 @@ Filename: {userappdata}\Rizonesoft\Notepad3\Notepad3.ini; Section: Settings; Key [Run] Filename: {app}\Notepad3.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked -Filename: "http://www.rizonesoft.com/downloads/"; Description: {cm:tsk_LaunchWelcomePage}; Flags: nowait postinstall shellexec skipifsilent [InstallDelete] @@ -174,7 +171,7 @@ Type: dirifempty; Name: {app} const IFEO = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe'; VersionURL = 'https://www.rizonesoft.com/update/Notepad3.rus'; - UpdateURL = 'https://www.rizonesoft.com/downloads/notepad3/update/'; + UpdateURL = 'https://goo.gl/y6CGMM'; type TIntegerArray = array of Integer; diff --git a/Build/Changes.txt b/Build/Changes.txt index 59fec5858..d6b0a5d38 100644 --- a/Build/Changes.txt +++ b/Build/Changes.txt @@ -2,6 +2,66 @@ Rizonesoft Notepad3 CHANGES ================================================== +-------------------------------------------------- +Version 3.18.113.828 (13 January 2018) +-------------------------------------------------- +- Performance: Redraw indicator ranges (Mark Occurrences) only if needed - avoiding recursive SCN_UPDATEUI notifications. +- Mark Occurrences: force position progress in case of zero-length matches. +- Performance: Mark Occurrence triggers enhancements. +- Convenience: Re-introduce "max mark occurrence counter" to avoid lazy UI. + (Set .ini section [Settings2] MarkOccurrencesMaxCount=-1 for unlimited (MAX_INT)). +- Fix: Correct counting of found matches. +- Fix: Broken find/replace (regex) on groups. +- Performance: Tuning delay parameter of "Mark Occurrences" (instantly). +- Performance: Remove test for set already, if indicator does not change. +- Fixing several Mark Occurrences (instantly) issues. +- Fix: Some customizing scheme issues (relative font size hierarchy). +- Fix: Document modified flag not set properly (title, toolbar & statusbar only). +- Fix: Renaming menu entries Edit: "Clear" -> "Delete" +- Enhancement: structured "Mark Occurrences" menu hierarchy. +- Fix: Menu "Mark Occurrences" whole word matching: add check indicator if one of word matching options (selected/current) is used. +- Change: Menu "Mark Occurrences" whole word matching: radio button behavior. +- Fixes Regarding Scheme customizing (rel. size) and View Menu (Mark Occ.). +- Fix: Regex replacement on look-ahead assertion. +- Fix: relative font sizing of non-default-style entries in custom schemes. +- Fix: Font selection box title display. +- Fix: clean coding for "relative sizing hierarchy". +- Feature: "Customize Schemes..." UI change to represent "relative sizing hierarchy". +- Fix: Update view on style change in "Customize schemes..." +- Fix: Bug regarding "Reset" button in "Customizing Schemes..." dialog. +- Fix: Bug in detect and style Hyperlink Hotspot. +- Feature: Initial version of Nim Lexer. Added Scintilla project and header for Nim lexer. +- Performance: Non visible styling during idle time. +- Performance: Cache page instead of line only. +- Change: Option "after visible" for styling in background (idle time). +- Update: Compiler version 191225834 (VS2017 V.15.5.3). +- Fix: Loosing state of "Transform Backslashes" while switching regex/wildcard search. +- Fix: Block "mark occurrences" event on transaction pair: (SCI_TARGETFROMSELECTIO, SCI_REPLACETARGET). +- Fix: protect SCI target transactions by a guard to break recursion of Mark Occurrences caused by ChangeNotification() events. +- Fix: Handle "2nd Default Styles" as separate standard (2nd) lexer module. +- Fix: Handling initial 1st or 2nd default style in case of "Default Text", shown on int "Customize Schemes...". +- Fix: Crash on navigation between schemes beyond start/end of tree view. +- Fix: Disable (using 1st Default Style) "Associated filename extensions" for "2nd Default Style". +- Fix: Clone associated filename extension list of Default Style (for 2nd Default Style). +- Enhancement: status bar information about usage of 2nd Default Style. +- Change: View "2nd Default Style" as (dbl-clickable) status bar item. +- Cleanup: NimLexer = NimrodLexer -> rely on Scintilla distribution for Nim(rod) lexer. +- Change: Word Wrap mode from WORD to SPACE to avoid wrapping at style change edges. +- Fix: move word wrap start symbol to margin (new WW mode SPACE (instead of WORD))". +- Fix: Mark Occurrences timeout on Find/Replace. +- Fix: Optimized status bar partitioning. +- Fix: Revert naming for "2nd Default Text". +- Clean Code: Reasonable assumption for memory allocation of style handling (backup, save, load). +- Tuning: Statusbar Partitioning. +- Fix: Bug on switching between 1st and 2nd standard lexers. +- Fix: Selection of 1st or 2nd standard lexer (Default Text) based on Use2ndStandard flag. +- Change: Onigmo RegEx syntax dialect. +- Change: Onigmo regex engine: use PERL 5.10 syntax + enable "\<" and "\>" word boundary matching. +- Fix: Onigmo regex engine PERL syntax: supporting named groups referencing in replacement text. +- Enhancement: Statusbar: Count number of bytes in current encoding. +- Change: Replace Windows Notepad Installer version only. +- Change: Executables not compressed with UPX. + -------------------------------------------------- Version 3.18.105.802 (5 January 2018) -------------------------------------------------- diff --git a/Build/notepad3_setup.iss b/Build/notepad3_setup.iss index 8c0871705..d05c74c8d 100644 --- a/Build/notepad3_setup.iss +++ b/Build/notepad3_setup.iss @@ -1,518 +1,529 @@ -;* Notepad3 - Installer script -;* -;* Copyright (C) 2008-2016 Rizonesoft - -; Requirements: -; Inno Setup: http://www.jrsoftware.org/isdl.php - -; Preprocessor related stuff -#if VER < EncodeVer(5,5,9) - #error Update your Inno Setup version (5.5.9 or newer) -#endif - -#define bindir "..\Bin" - -#ifnexist bindir + "\Release_x86_v141\Notepad3.exe" - #error Compile Notepad3 x86 first -#endif - -#ifnexist bindir + "\Release_x86_v141\minipath.exe" - #error Compile MiniPath x86 first -#endif - -#ifnexist bindir + "\Release_x86_v141\np3encrypt.exe" - #error Compile np3encrypt.exe x86 first -#endif - -#ifnexist bindir + "\Release_x64_v141\Notepad3.exe" - #error Compile Notepad3 x64 first -#endif - -#ifnexist bindir + "\Release_x64_v141\minipath.exe" - #error Compile MiniPath x64 first -#endif - -#ifnexist bindir + "\Release_x64_v141\np3encrypt.exe" - #error Compile np3encrypt.exe x64 first -#endif - -#define app_version GetFileVersion(bindir + "\Release_x86_v141\Notepad3.exe") -#define app_name "Notepad3" -#define app_copyright "Copyright © 2008-2016, Rizonesoft." -#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch" - -[Setup] -AppId={#app_name} -AppName={#app_name} -AppVersion={#app_version} -AppVerName={#app_name} {#app_version} -AppPublisher=Rizonesoft -AppPublisherURL=https://rizonesoft.com -AppSupportURL=https://rizonesoft.com -AppUpdatesURL=https://rizonesoft.com -AppContact=https://rizonesoft.com -AppCopyright={#app_copyright} -;VersionInfoVersion={#app_version} -UninstallDisplayIcon={app}\Notepad3.exe -UninstallDisplayName={#app_name} {#app_version} -DefaultDirName={pf}\Notepad3 -LicenseFile=License.txt -OutputDir=.\Packages -OutputBaseFilename={#app_name}_{#app_version} -SetupIconFile=.\Resources\Setup.ico -WizardImageFile=compiler:WizModernImage-IS.bmp -WizardSmallImageFile=.\Resources\WizardSmallImageFile.bmp -Compression=lzma2/max -InternalCompressLevel=max -SolidCompression=yes -EnableDirDoesntExistWarning=no -AllowNoIcons=yes -ShowTasksTreeLines=yes -DisableDirPage=yes -DisableProgramGroupPage=yes -DisableReadyPage=yes -DisableWelcomePage=yes -AllowCancelDuringInstall=no -MinVersion=5.1sp3 -ArchitecturesAllowed=x86 x64 -ArchitecturesInstallIn64BitMode=x64 -#ifexist "..\signinfo_notepad3.txt" -SignTool=MySignTool -#endif -CloseApplications=true -SetupMutex='{#app_name}' + '_setup_mutex' - - -[Languages] -Name: en; MessagesFile: compiler:Default.isl - - -[Messages] -;BeveledLabel ={#app_name} {#app_version} - Compiled with VC2015 -SetupAppTitle =Setup - {#app_name} -SetupWindowTitle =Setup - {#app_name} - - -[CustomMessages] -en.msg_AppIsRunning =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -en.msg_DeleteSettings =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them. -#if defined(sse_required) -en.msg_simd_sse =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities. -#elif defined(sse2_required) -en.msg_simd_sse2 =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities. -#endif -en.tsk_AllUsers =For all users -en.tsk_CurrentUser =For the current user only -en.tsk_Other =Other tasks: -en.tsk_ResetSettings =Reset {#app_name}'s settings -en.tsk_RemoveDefault =Restore Windows notepad -en.tsk_StartMenuIcon =Create a Start Menu shortcut - - -[Tasks] -Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked -Name: desktopicon\user; Description: {cm:tsk_CurrentUser}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive -Name: desktopicon\common; Description: {cm:tsk_AllUsers}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive -Name: startup_icon; Description: {cm:tsk_StartMenuIcon}; GroupDescription: {cm:AdditionalIcons} -Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 6.01 -Name: reset_settings; Description: {cm:tsk_ResetSettings}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: SettingsExistCheck() -Name: remove_default; Description: {cm:tsk_RemoveDefault}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: DefaulNotepadCheck() - -[Files] -Source: {#bindir}\Release_x64_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() -Source: License.txt; DestDir: {app}; Flags: ignoreversion -Source: Readme.txt; DestDir: {app}; Flags: ignoreversion -Source: Notepad3.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall -Source: {#bindir}\Release_x64_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() -Source: minipath.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall -Source: {#bindir}\Release_x64_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() - -[Dirs] -Name: "{userappdata}\Rizonesoft\Notepad3\Favorites" - -[Icons] -Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {userdesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\user; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {userstartmenu}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: startup_icon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: quicklaunchicon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; IconFilename: {app}\Notepad3.exe; IconIndex: 0 - - -[INI] -Filename: {app}\Notepad3.ini; Section: Notepad3; Key: Notepad3.ini; String: %APPDATA%\Rizonesoft\Notepad3\Notepad3.ini -Filename: {app}\minipath.ini; Section: minipath; Key: minipath.ini; String: %APPDATA%\Rizonesoft\Notepad3\minipath.ini - -Filename: {userappdata}\Rizonesoft\Notepad3\Notepad3.ini; Section: Settings; Key: Favorites; String: %APPDATA%\Rizonesoft\Notepad3\Favorites\ - - -[Run] -Filename: {app}\Notepad3.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked - - -[InstallDelete] -Type: files; Name: {userdesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\user') and IsUpgrade() -Type: files; Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade() -Type: files; Name: {userstartmenu}\{#app_name}.lnk; Check: not IsTaskSelected('startup_icon') and IsUpgrade() -Type: files; Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon') and IsUpgrade(); OnlyBelowVersion: 6.01 -Type: files; Name: {app}\Notepad3.ini -Type: files; Name: {app}\Readme.txt -Type: files; Name: {app}\minipath.ini - - -[UninstallDelete] -Type: files; Name: {app}\Notepad3.ini -Type: files; Name: {app}\minipath.ini -Type: dirifempty; Name: {app} - -[Code] -const - IFEO = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe'; - VersionURL = 'https://www.rizonesoft.com/update/Notepad3.rus'; - UpdateURL = 'https://goo.gl/y6CGMM'; - -type - TIntegerArray = array of Integer; - TCompareResult = ( - crLesser, - crEquals, - crGreater - ); - -function Max(A, B: Integer): Integer; -begin - if A > B then Result := A else Result := B; -end; - -function CompareValue(A, B: Integer): TCompareResult; -begin - if A = B then - Result := crEquals - else - if A < B then - Result := crLesser - else - Result := crGreater; -end; - -function AddVersionChunk(const S: string; var A: TIntegerArray): Integer; -var - Chunk: Integer; -begin - Chunk := StrToIntDef(S, -1); - if Chunk <> -1 then - begin - Result := GetArrayLength(A) + 1; - SetArrayLength(A, Result); - A[Result - 1] := Chunk; - end - else - RaiseException('Invalid format of version string'); -end; - -function ParseVersionStr(const S: string; var A: TIntegerArray): Integer; -var - I: Integer; - Count: Integer; - Index: Integer; -begin - Count := 0; - Index := 1; - - for I := 1 to Length(S) do - begin - case S[I] of - '.': - begin - AddVersionChunk(Copy(S, Index, Count), A); - Count := 0; - Index := I + 1; - end; - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - begin - Count := Count + 1; - end; - else - RaiseException('Invalid char in version string'); - end; - end; - Result := AddVersionChunk(Copy(S, Index, Count), A); -end; - -function GetVersionValue(const A: TIntegerArray; Index, - Length: Integer): Integer; -begin - Result := 0; - if (Index >= 0) and (Index < Length) then - Result := A[Index]; -end; - -function CompareVersionStr(const A, B: string): TCompareResult; -var - I: Integer; - VerLenA, VerLenB: Integer; - VerIntA, VerIntB: TIntegerArray; -begin - Result := crEquals; - - VerLenA := ParseVersionStr(A, VerIntA); - VerLenB := ParseVersionStr(B, VerIntB); - - for I := 0 to Max(VerLenA, VerLenB) - 1 do - begin - Result := CompareValue(GetVersionValue(VerIntA, I, VerLenA), - GetVersionValue(VerIntB, I, VerLenB)); - if Result <> crEquals then - Exit; - end; -end; - -function DownloadFile(const URL: string; var Response: string): Boolean; -var - WinHttpRequest: Variant; -begin - Result := True; - try - WinHttpRequest := CreateOleObject('WinHttp.WinHttpRequest.5.1'); - WinHttpRequest.Open('GET', URL, False); - WinHttpRequest.Send; - Response := WinHttpRequest.ResponseText; - except - Result := False; - Response := GetExceptionMessage; - end; -end; - -function InitializeSetup: Boolean; -var - ErrorCode: Integer; - SetupVersion: string; - LatestVersion: string; - -begin - Result := True; - - //Check for Processor SSE2 support. - #if defined(sse2_required) - if not IsSSE2Supported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; - #elif defined(sse_required) - if not IsSSESupported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; - #endif - - if DownloadFile(VersionURL, LatestVersion) then - begin - SetupVersion := '{#SetupSetting('AppVersion')}'; - if CompareVersionStr(LatestVersion, SetupVersion) = crGreater then - begin - if MsgBox('There is a newer version of {#SetupSetting('AppName')} available. Do ' + - 'you want to visit the site?', mbConfirmation, MB_YESNO) = IDYES then - begin - Result := not ShellExec('', UpdateURL, '', '', SW_SHOW, ewNoWait, - ErrorCode); - end; - end; - end; - -end; - -// Check if Notepad3 has replaced Windows Notepad -function DefaulNotepadCheck(): Boolean; -var - sDebugger: String; -begin - if RegQueryStringValue(HKLM, IFEO, 'Debugger', sDebugger) and - (sDebugger = (ExpandConstant('"{app}\Notepad3.exe" /z'))) then begin - Log('Custom Code: {#app_name} is set as the default notepad'); - Result := True; - end - else begin - Log('Custom Code: {#app_name} is NOT set as the default notepad'); - Result := False; - end; -end; - -#if defined(sse_required) || defined(sse2_required) -function IsProcessorFeaturePresent(Feature: Integer): Boolean; -external 'IsProcessorFeaturePresent@kernel32.dll stdcall'; -#endif - -#if defined(sse_required) -function IsSSESupported(): Boolean; -begin - // PF_XMMI_INSTRUCTIONS_AVAILABLE - Result := IsProcessorFeaturePresent(6); -end; - -#elif defined(sse2_required) - -function IsSSE2Supported(): Boolean; -begin - // PF_XMMI64_INSTRUCTIONS_AVAILABLE - Result := IsProcessorFeaturePresent(10); -end; - -#endif - - -function IsOldBuildInstalled(sInfFile: String): Boolean; -begin - if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and - FileExists(ExpandConstant('{pf}\Notepad2\' + sInfFile)) then - Result := True - else - Result := False; -end; - - -function IsUpgrade(): Boolean; -var - sPrevPath: String; -begin - sPrevPath := WizardForm.PrevAppDir; - Result := (sPrevPath <> ''); -end; - - -// Check if Notepad3's settings exist -function SettingsExistCheck(): Boolean; -begin - if FileExists(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')) then begin - Log('Custom Code: Settings are present'); - Result := True; - end - else begin - Log('Custom Code: Settings are NOT present'); - Result := False; - end; -end; - - -function UninstallOldVersion(sInfFile: String): Integer; -var - iResultCode: Integer; -begin - // Return Values: - // 0 - no idea - // 1 - error executing the command - // 2 - successfully executed the command - - // default return value - Result := 0; - // TODO: use RegQueryStringValue - if not Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx ' + '"{pf}\Notepad2\' + sInfFile +'",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin - Result := 1; - end - else begin - Result := 2; - Sleep(200); - end; -end; - - -function ShouldSkipPage(PageID: Integer): Boolean; -begin - // Hide the license page if IsUpgrade() - if IsUpgrade() and (PageID = wpLicense) then - Result := True; -end; - - -procedure AddReg(); -begin - RegWriteStringValue(HKCR, 'Applications\notepad3.exe', 'AppUserModelID', 'Notepad3'); - RegWriteStringValue(HKCR, 'Applications\notepad3.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad3.exe" %1')); - RegWriteStringValue(HKCR, '*\OpenWithList\notepad3.exe', '', ''); -end; - - -procedure CleanUpSettings(); -begin - DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')); - DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\minipath.ini')); - RemoveDir(ExpandConstant('{userappdata}\Rizonesoft\Notepad3')); -end; - - -procedure RemoveReg(); -begin - RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad3.exe'); - RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad3.exe'); -end; - - -procedure CurPageChanged(CurPageID: Integer); -begin - if CurPageID = wpSelectTasks then - WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) - else if CurPageID = wpFinished then - WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish); -end; - - -procedure CurStepChanged(CurStep: TSetupStep); -begin - if CurStep = ssInstall then begin - if IsTaskSelected('reset_settings') then - CleanUpSettings(); - - if IsOldBuildInstalled('Uninstall.inf') or IsOldBuildInstalled('Notepad2.inf') then begin - if IsOldBuildInstalled('Uninstall.inf') then begin - Log('Custom Code: The old build is installed, will try to uninstall it'); - if UninstallOldVersion('Uninstall.inf') = 2 then - Log('Custom Code: The old build was successfully uninstalled') - else - Log('Custom Code: Something went wrong when uninstalling the old build'); - end; - - if IsOldBuildInstalled('Notepad2.inf') then begin - Log('Custom Code: The official Notepad2 build is installed, will try to uninstall it'); - if UninstallOldVersion('Notepad2.inf') = 2 then - Log('Custom Code: The official Notepad2 build was successfully uninstalled') - else - Log('Custom Code: Something went wrong when uninstalling the official Notepad2 build'); - end; - end; - end; - - if CurStep = ssPostInstall then begin - if IsTaskSelected('remove_default') then begin - RegDeleteValue(HKLM, IFEO, 'Debugger'); - RegDeleteKeyIfEmpty(HKLM, IFEO); - end; - // Always add Notepad3's AppUserModelID and the rest registry values - AddReg(); - end; - -end; - - -procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); -begin - // When uninstalling, ask the user to delete Notepad3's settings - if CurUninstallStep = usUninstall then begin - if SettingsExistCheck() then begin - if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then - CleanUpSettings(); - end; - if DefaulNotepadCheck() then - RegDeleteValue(HKLM, IFEO, 'Debugger'); - RegDeleteKeyIfEmpty(HKLM, IFEO); - RemoveReg(); - end; -end; - - -procedure InitializeWizard(); -begin - WizardForm.SelectTasksLabel.Hide; - WizardForm.TasksList.Top := 0; - WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight; -end; +;* Notepad3 - Installer script +;* +;* Copyright (C) 2008-2016 Rizonesoft + +; Requirements: +; Inno Setup: http://www.jrsoftware.org/isdl.php + +; Preprocessor related stuff +#if VER < EncodeVer(5,5,9) + #error Update your Inno Setup version (5.5.9 or newer) +#endif + +#define bindir "..\Bin" + +#ifnexist bindir + "\Release_x86_v141\Notepad3.exe" + #error Compile Notepad3 x86 first +#endif + +#ifnexist bindir + "\Release_x86_v141\minipath.exe" + #error Compile MiniPath x86 first +#endif + +#ifnexist bindir + "\Release_x86_v141\np3encrypt.exe" + #error Compile np3encrypt.exe x86 first +#endif + +#ifnexist bindir + "\Release_x64_v141\Notepad3.exe" + #error Compile Notepad3 x64 first +#endif + +#ifnexist bindir + "\Release_x64_v141\minipath.exe" + #error Compile MiniPath x64 first +#endif + #ifnexist bindir + "\Release_x64_v141\np3encrypt.exe" + #error Compile np3encrypt.exe x64 first +#endif + +#define app_version GetFileVersion(bindir + "\Release_x86_v141\Notepad3.exe") +#define app_name "Notepad3" +#define app_copyright "Copyright © 2008-2016, Rizonesoft." +#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch" + +[Setup] +AppId={#app_name} +AppName={#app_name} +AppVersion={#app_version} +AppVerName={#app_name} {#app_version} +AppPublisher=Rizonesoft +AppPublisherURL=https://rizonesoft.com +AppSupportURL=https://rizonesoft.com +AppUpdatesURL=https://rizonesoft.com +AppContact=https://rizonesoft.com +AppCopyright={#app_copyright} +;VersionInfoVersion={#app_version} +UninstallDisplayIcon={app}\Notepad3.exe +UninstallDisplayName={#app_name} {#app_version} +DefaultDirName={pf}\Notepad3 +LicenseFile=License.txt +OutputDir=.\Packages +OutputBaseFilename={#app_name}_{#app_version} +SetupIconFile=.\Resources\Setup.ico +WizardImageFile=compiler:WizModernImage-IS.bmp +WizardSmallImageFile=.\Resources\WizardSmallImageFile.bmp +Compression=lzma2/max +InternalCompressLevel=max +SolidCompression=yes +EnableDirDoesntExistWarning=no +AllowNoIcons=yes +ShowTasksTreeLines=yes +DisableDirPage=yes +DisableProgramGroupPage=yes +DisableReadyPage=yes +DisableWelcomePage=yes +AllowCancelDuringInstall=no +MinVersion=5.1sp3 +ArchitecturesAllowed=x86 x64 +ArchitecturesInstallIn64BitMode=x64 +#ifexist "..\signinfo_notepad3.txt" +SignTool=MySignTool +#endif +CloseApplications=true +SetupMutex='{#app_name}' + '_setup_mutex' + + +[Languages] +Name: en; MessagesFile: compiler:Default.isl + + +[Messages] +;BeveledLabel ={#app_name} {#app_version} - Compiled with VC2015 +SetupAppTitle =Setup - {#app_name} +SetupWindowTitle =Setup - {#app_name} + + +[CustomMessages] +en.msg_AppIsRunning =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. +en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. +en.msg_DeleteSettings =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them. +#if defined(sse_required) +en.msg_simd_sse =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities. +#elif defined(sse2_required) +en.msg_simd_sse2 =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities. +#endif +en.tsk_AllUsers =For all users +en.tsk_CurrentUser =For the current user only +en.tsk_Other =Other tasks: +en.tsk_ResetSettings =Reset {#app_name}'s settings +en.tsk_RemoveDefault =Restore Windows notepad +en.tsk_SetDefault =Replace Windows notepad with {#app_name} +en.tsk_StartMenuIcon =Create a Start Menu shortcut +en.tsk_LaunchWelcomePage =Get more from Rizonesoft + + +[Tasks] +Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked +Name: desktopicon\user; Description: {cm:tsk_CurrentUser}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive +Name: desktopicon\common; Description: {cm:tsk_AllUsers}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive +Name: startup_icon; Description: {cm:tsk_StartMenuIcon}; GroupDescription: {cm:AdditionalIcons} +Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 6.01 +Name: reset_settings; Description: {cm:tsk_ResetSettings}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: SettingsExistCheck() +Name: set_default; Description: {cm:tsk_SetDefault}; GroupDescription: {cm:tsk_Other}; Check: not DefaulNotepadCheck() +Name: remove_default; Description: {cm:tsk_RemoveDefault}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: DefaulNotepadCheck() + + +[Files] +Source: {#bindir}\Release_x64_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() +Source: {#bindir}\Release_x86_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() +Source: License.txt; DestDir: {app}; Flags: ignoreversion +Source: Readme.txt; DestDir: {app}; Flags: ignoreversion +Source: Notepad3.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall +Source: {#bindir}\Release_x64_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() +Source: {#bindir}\Release_x86_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() +Source: minipath.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall +Source: {#bindir}\Release_x64_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() +Source: {#bindir}\Release_x86_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() + +[Dirs] +Name: "{userappdata}\Rizonesoft\Notepad3\Favorites" + +[Icons] +Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 +Name: {userdesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\user; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 +Name: {userstartmenu}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: startup_icon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 +Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: quicklaunchicon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; IconFilename: {app}\Notepad3.exe; IconIndex: 0 + + +[INI] +Filename: {app}\Notepad3.ini; Section: Notepad3; Key: Notepad3.ini; String: %APPDATA%\Rizonesoft\Notepad3\Notepad3.ini +Filename: {app}\minipath.ini; Section: minipath; Key: minipath.ini; String: %APPDATA%\Rizonesoft\Notepad3\minipath.ini +Filename: {userappdata}\Rizonesoft\Notepad3\Notepad3.ini; Section: Settings; Key: Favorites; String: %APPDATA%\Rizonesoft\Notepad3\Favorites\ + + +[Run] +Filename: {app}\Notepad3.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked +Filename: https://goo.gl/tGtJ6a; Description: {cm:tsk_LaunchWelcomePage}; Flags: nowait postinstall shellexec skipifsilent + + +[InstallDelete] +Type: files; Name: {userdesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\user') and IsUpgrade() +Type: files; Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade() +Type: files; Name: {userstartmenu}\{#app_name}.lnk; Check: not IsTaskSelected('startup_icon') and IsUpgrade() +Type: files; Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon') and IsUpgrade(); OnlyBelowVersion: 6.01 +Type: files; Name: {app}\Notepad3.ini +Type: files; Name: {app}\Readme.txt +Type: files; Name: {app}\minipath.ini + + +[UninstallDelete] +Type: files; Name: {app}\Notepad3.ini +Type: files; Name: {app}\minipath.ini +Type: dirifempty; Name: {app} + +[Code] +const + IFEO = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe'; + VersionURL = 'https://www.rizonesoft.com/update/Notepad3.rus'; + UpdateURL = 'https://goo.gl/y6CGMM'; + +type + TIntegerArray = array of Integer; + TCompareResult = ( + crLesser, + crEquals, + crGreater + ); + +function Max(A, B: Integer): Integer; +begin + if A > B then Result := A else Result := B; +end; + +function CompareValue(A, B: Integer): TCompareResult; +begin + if A = B then + Result := crEquals + else + if A < B then + Result := crLesser + else + Result := crGreater; +end; + +function AddVersionChunk(const S: string; var A: TIntegerArray): Integer; +var + Chunk: Integer; +begin + Chunk := StrToIntDef(S, -1); + if Chunk <> -1 then + begin + Result := GetArrayLength(A) + 1; + SetArrayLength(A, Result); + A[Result - 1] := Chunk; + end + else + RaiseException('Invalid format of version string'); +end; + +function ParseVersionStr(const S: string; var A: TIntegerArray): Integer; +var + I: Integer; + Count: Integer; + Index: Integer; +begin + Count := 0; + Index := 1; + + for I := 1 to Length(S) do + begin + case S[I] of + '.': + begin + AddVersionChunk(Copy(S, Index, Count), A); + Count := 0; + Index := I + 1; + end; + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + begin + Count := Count + 1; + end; + else + RaiseException('Invalid char in version string'); + end; + end; + Result := AddVersionChunk(Copy(S, Index, Count), A); +end; + +function GetVersionValue(const A: TIntegerArray; Index, + Length: Integer): Integer; +begin + Result := 0; + if (Index >= 0) and (Index < Length) then + Result := A[Index]; +end; + +function CompareVersionStr(const A, B: string): TCompareResult; +var + I: Integer; + VerLenA, VerLenB: Integer; + VerIntA, VerIntB: TIntegerArray; +begin + Result := crEquals; + + VerLenA := ParseVersionStr(A, VerIntA); + VerLenB := ParseVersionStr(B, VerIntB); + + for I := 0 to Max(VerLenA, VerLenB) - 1 do + begin + Result := CompareValue(GetVersionValue(VerIntA, I, VerLenA), + GetVersionValue(VerIntB, I, VerLenB)); + if Result <> crEquals then + Exit; + end; +end; + +function DownloadFile(const URL: string; var Response: string): Boolean; +var + WinHttpRequest: Variant; +begin + Result := True; + try + WinHttpRequest := CreateOleObject('WinHttp.WinHttpRequest.5.1'); + WinHttpRequest.Open('GET', URL, False); + WinHttpRequest.Send; + Response := WinHttpRequest.ResponseText; + except + Result := False; + Response := GetExceptionMessage; + end; +end; + +function InitializeSetup: Boolean; +var + ErrorCode: Integer; + SetupVersion: string; + LatestVersion: string; + +begin + Result := True; + + //Check for Processor SSE2 support. + #if defined(sse2_required) + if not IsSSE2Supported() then begin + SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK); + Result := False; + end; + #elif defined(sse_required) + if not IsSSESupported() then begin + SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK); + Result := False; + end; + #endif + + if DownloadFile(VersionURL, LatestVersion) then + begin + SetupVersion := '{#SetupSetting('AppVersion')}'; + if CompareVersionStr(LatestVersion, SetupVersion) = crGreater then + begin + if MsgBox('There is a newer version of {#SetupSetting('AppName')} available. Do ' + + 'you want to visit the site?', mbConfirmation, MB_YESNO) = IDYES then + begin + Result := not ShellExec('', UpdateURL, '', '', SW_SHOW, ewNoWait, + ErrorCode); + end; + end; + end; + +end; + +// Check if Notepad3 has replaced Windows Notepad +function DefaulNotepadCheck(): Boolean; +var + sDebugger: String; +begin + if RegQueryStringValue(HKLM, IFEO, 'Debugger', sDebugger) and + (sDebugger = (ExpandConstant('"{app}\Notepad3.exe" /z'))) then begin + Log('Custom Code: {#app_name} is set as the default notepad'); + Result := True; + end + else begin + Log('Custom Code: {#app_name} is NOT set as the default notepad'); + Result := False; + end; +end; + +#if defined(sse_required) || defined(sse2_required) +function IsProcessorFeaturePresent(Feature: Integer): Boolean; +external 'IsProcessorFeaturePresent@kernel32.dll stdcall'; +#endif + +#if defined(sse_required) +function IsSSESupported(): Boolean; +begin + // PF_XMMI_INSTRUCTIONS_AVAILABLE + Result := IsProcessorFeaturePresent(6); +end; + +#elif defined(sse2_required) + +function IsSSE2Supported(): Boolean; +begin + // PF_XMMI64_INSTRUCTIONS_AVAILABLE + Result := IsProcessorFeaturePresent(10); +end; + +#endif + + +function IsOldBuildInstalled(sInfFile: String): Boolean; +begin + if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and + FileExists(ExpandConstant('{pf}\Notepad2\' + sInfFile)) then + Result := True + else + Result := False; +end; + + +function IsUpgrade(): Boolean; +var + sPrevPath: String; +begin + sPrevPath := WizardForm.PrevAppDir; + Result := (sPrevPath <> ''); +end; + + +// Check if Notepad3's settings exist +function SettingsExistCheck(): Boolean; +begin + if FileExists(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')) then begin + Log('Custom Code: Settings are present'); + Result := True; + end + else begin + Log('Custom Code: Settings are NOT present'); + Result := False; + end; +end; + + +function UninstallOldVersion(sInfFile: String): Integer; +var + iResultCode: Integer; +begin + // Return Values: + // 0 - no idea + // 1 - error executing the command + // 2 - successfully executed the command + + // default return value + Result := 0; + // TODO: use RegQueryStringValue + if not Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx ' + '"{pf}\Notepad2\' + sInfFile +'",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin + Result := 1; + end + else begin + Result := 2; + Sleep(200); + end; +end; + + +function ShouldSkipPage(PageID: Integer): Boolean; +begin + // Hide the license page if IsUpgrade() + if IsUpgrade() and (PageID = wpLicense) then + Result := True; +end; + + +procedure AddReg(); +begin + RegWriteStringValue(HKCR, 'Applications\notepad3.exe', 'AppUserModelID', 'Notepad3'); + RegWriteStringValue(HKCR, 'Applications\notepad3.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad3.exe" %1')); + RegWriteStringValue(HKCR, '*\OpenWithList\notepad3.exe', '', ''); +end; + + +procedure CleanUpSettings(); +begin + DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')); + DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\minipath.ini')); + RemoveDir(ExpandConstant('{userappdata}\Rizonesoft\Notepad3')); +end; + + +procedure RemoveReg(); +begin + RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad3.exe'); + RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad3.exe'); +end; + + +procedure CurPageChanged(CurPageID: Integer); +begin + if CurPageID = wpSelectTasks then + WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) + else if CurPageID = wpFinished then + WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish); +end; + + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssInstall then begin + if IsTaskSelected('reset_settings') then + CleanUpSettings(); + + if IsOldBuildInstalled('Uninstall.inf') or IsOldBuildInstalled('Notepad2.inf') then begin + if IsOldBuildInstalled('Uninstall.inf') then begin + Log('Custom Code: The old build is installed, will try to uninstall it'); + if UninstallOldVersion('Uninstall.inf') = 2 then + Log('Custom Code: The old build was successfully uninstalled') + else + Log('Custom Code: Something went wrong when uninstalling the old build'); + end; + + if IsOldBuildInstalled('Notepad2.inf') then begin + Log('Custom Code: The official Notepad2 build is installed, will try to uninstall it'); + if UninstallOldVersion('Notepad2.inf') = 2 then + Log('Custom Code: The official Notepad2 build was successfully uninstalled') + else + Log('Custom Code: Something went wrong when uninstalling the official Notepad2 build'); + end; + + // This is the case where the old build is installed; the DefaulNotepadCheck() returns true + // and the set_default task isn't selected + if not IsTaskSelected('remove_default') then + RegWriteStringValue(HKLM, IFEO, 'Debugger', ExpandConstant('"{app}\Notepad3.exe" /z')); + + end; + end; + + if CurStep = ssPostInstall then begin + if IsTaskSelected('set_default') then + RegWriteStringValue(HKLM, IFEO, 'Debugger', ExpandConstant('"{app}\Notepad3.exe" /z')); + if IsTaskSelected('remove_default') then begin + RegDeleteValue(HKLM, IFEO, 'Debugger'); + RegDeleteKeyIfEmpty(HKLM, IFEO); + end; + // Always add Notepad3's AppUserModelID and the rest registry values + AddReg(); + end; + +end; + + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + // When uninstalling, ask the user to delete Notepad3's settings + if CurUninstallStep = usUninstall then begin + if SettingsExistCheck() then begin + if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then + CleanUpSettings(); + end; + if DefaulNotepadCheck() then + RegDeleteValue(HKLM, IFEO, 'Debugger'); + RegDeleteKeyIfEmpty(HKLM, IFEO); + RemoveReg(); + end; +end; + + +procedure InitializeWizard(); +begin + WizardForm.SelectTasksLabel.Hide; + WizardForm.TasksList.Top := 0; + WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight; +end; diff --git a/Build/notepad3_setup_replace.iss b/Build/notepad3_setup_replace.iss deleted file mode 100644 index 71aa10528..000000000 --- a/Build/notepad3_setup_replace.iss +++ /dev/null @@ -1,529 +0,0 @@ -;* Notepad3 - Installer script -;* -;* Copyright (C) 2008-2016 Rizonesoft - -; Requirements: -; Inno Setup: http://www.jrsoftware.org/isdl.php - -; Preprocessor related stuff -#if VER < EncodeVer(5,5,9) - #error Update your Inno Setup version (5.5.9 or newer) -#endif - -#define bindir "..\Bin" - -#ifnexist bindir + "\Release_x86_v141\Notepad3.exe" - #error Compile Notepad3 x86 first -#endif - -#ifnexist bindir + "\Release_x86_v141\minipath.exe" - #error Compile MiniPath x86 first -#endif - -#ifnexist bindir + "\Release_x86_v141\np3encrypt.exe" - #error Compile np3encrypt.exe x86 first -#endif - -#ifnexist bindir + "\Release_x64_v141\Notepad3.exe" - #error Compile Notepad3 x64 first -#endif - -#ifnexist bindir + "\Release_x64_v141\minipath.exe" - #error Compile MiniPath x64 first -#endif - #ifnexist bindir + "\Release_x64_v141\np3encrypt.exe" - #error Compile np3encrypt.exe x64 first -#endif - -#define app_version GetFileVersion(bindir + "\Release_x86_v141\Notepad3.exe") -#define app_name "Notepad3" -#define app_copyright "Copyright © 2008-2016, Rizonesoft." -#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch" - -[Setup] -AppId={#app_name} -AppName={#app_name} -AppVersion={#app_version} -AppVerName={#app_name} {#app_version} -AppPublisher=Rizonesoft -AppPublisherURL=https://rizonesoft.com -AppSupportURL=https://rizonesoft.com -AppUpdatesURL=https://rizonesoft.com -AppContact=https://rizonesoft.com -AppCopyright={#app_copyright} -;VersionInfoVersion={#app_version} -UninstallDisplayIcon={app}\Notepad3.exe -UninstallDisplayName={#app_name} {#app_version} -DefaultDirName={pf}\Notepad3 -LicenseFile=License.txt -OutputDir=.\Packages -OutputBaseFilename={#app_name}_x_{#app_version} -SetupIconFile=.\Resources\Setup.ico -WizardImageFile=compiler:WizModernImage-IS.bmp -WizardSmallImageFile=.\Resources\WizardSmallImageFile.bmp -Compression=lzma2/max -InternalCompressLevel=max -SolidCompression=yes -EnableDirDoesntExistWarning=no -AllowNoIcons=yes -ShowTasksTreeLines=yes -DisableDirPage=yes -DisableProgramGroupPage=yes -DisableReadyPage=yes -DisableWelcomePage=yes -AllowCancelDuringInstall=no -MinVersion=5.1sp3 -ArchitecturesAllowed=x86 x64 -ArchitecturesInstallIn64BitMode=x64 -#ifexist "..\signinfo_notepad3.txt" -SignTool=MySignTool -#endif -CloseApplications=true -SetupMutex='{#app_name}' + '_setup_mutex' - - -[Languages] -Name: en; MessagesFile: compiler:Default.isl - - -[Messages] -;BeveledLabel ={#app_name} {#app_version} - Compiled with VC2015 -SetupAppTitle =Setup - {#app_name} -SetupWindowTitle =Setup - {#app_name} - - -[CustomMessages] -en.msg_AppIsRunning =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -en.msg_DeleteSettings =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them. -#if defined(sse_required) -en.msg_simd_sse =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities. -#elif defined(sse2_required) -en.msg_simd_sse2 =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities. -#endif -en.tsk_AllUsers =For all users -en.tsk_CurrentUser =For the current user only -en.tsk_Other =Other tasks: -en.tsk_ResetSettings =Reset {#app_name}'s settings -en.tsk_RemoveDefault =Restore Windows notepad -en.tsk_SetDefault =Replace Windows notepad with {#app_name} -en.tsk_StartMenuIcon =Create a Start Menu shortcut -en.tsk_LaunchWelcomePage =Get more from Rizonesoft - - -[Tasks] -Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked -Name: desktopicon\user; Description: {cm:tsk_CurrentUser}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive -Name: desktopicon\common; Description: {cm:tsk_AllUsers}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive -Name: startup_icon; Description: {cm:tsk_StartMenuIcon}; GroupDescription: {cm:AdditionalIcons} -Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 6.01 -Name: reset_settings; Description: {cm:tsk_ResetSettings}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: SettingsExistCheck() -Name: set_default; Description: {cm:tsk_SetDefault}; GroupDescription: {cm:tsk_Other}; Check: not DefaulNotepadCheck() -Name: remove_default; Description: {cm:tsk_RemoveDefault}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: DefaulNotepadCheck() - - -[Files] -Source: {#bindir}\Release_x64_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() -Source: License.txt; DestDir: {app}; Flags: ignoreversion -Source: Readme.txt; DestDir: {app}; Flags: ignoreversion -Source: Notepad3.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall -Source: {#bindir}\Release_x64_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() -Source: minipath.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall -Source: {#bindir}\Release_x64_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() - -[Dirs] -Name: "{userappdata}\Rizonesoft\Notepad3\Favorites" - -[Icons] -Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {userdesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\user; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {userstartmenu}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: startup_icon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: quicklaunchicon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; IconFilename: {app}\Notepad3.exe; IconIndex: 0 - - -[INI] -Filename: {app}\Notepad3.ini; Section: Notepad3; Key: Notepad3.ini; String: %APPDATA%\Rizonesoft\Notepad3\Notepad3.ini -Filename: {app}\minipath.ini; Section: minipath; Key: minipath.ini; String: %APPDATA%\Rizonesoft\Notepad3\minipath.ini -Filename: {userappdata}\Rizonesoft\Notepad3\Notepad3.ini; Section: Settings; Key: Favorites; String: %APPDATA%\Rizonesoft\Notepad3\Favorites\ - - -[Run] -Filename: {app}\Notepad3.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked -Filename: https://goo.gl/tGtJ6a; Description: {cm:tsk_LaunchWelcomePage}; Flags: nowait postinstall shellexec skipifsilent - - -[InstallDelete] -Type: files; Name: {userdesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\user') and IsUpgrade() -Type: files; Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade() -Type: files; Name: {userstartmenu}\{#app_name}.lnk; Check: not IsTaskSelected('startup_icon') and IsUpgrade() -Type: files; Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon') and IsUpgrade(); OnlyBelowVersion: 6.01 -Type: files; Name: {app}\Notepad3.ini -Type: files; Name: {app}\Readme.txt -Type: files; Name: {app}\minipath.ini - - -[UninstallDelete] -Type: files; Name: {app}\Notepad3.ini -Type: files; Name: {app}\minipath.ini -Type: dirifempty; Name: {app} - -[Code] -const - IFEO = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe'; - VersionURL = 'https://www.rizonesoft.com/update/Notepad3.rus'; - UpdateURL = 'https://goo.gl/y6CGMM'; - -type - TIntegerArray = array of Integer; - TCompareResult = ( - crLesser, - crEquals, - crGreater - ); - -function Max(A, B: Integer): Integer; -begin - if A > B then Result := A else Result := B; -end; - -function CompareValue(A, B: Integer): TCompareResult; -begin - if A = B then - Result := crEquals - else - if A < B then - Result := crLesser - else - Result := crGreater; -end; - -function AddVersionChunk(const S: string; var A: TIntegerArray): Integer; -var - Chunk: Integer; -begin - Chunk := StrToIntDef(S, -1); - if Chunk <> -1 then - begin - Result := GetArrayLength(A) + 1; - SetArrayLength(A, Result); - A[Result - 1] := Chunk; - end - else - RaiseException('Invalid format of version string'); -end; - -function ParseVersionStr(const S: string; var A: TIntegerArray): Integer; -var - I: Integer; - Count: Integer; - Index: Integer; -begin - Count := 0; - Index := 1; - - for I := 1 to Length(S) do - begin - case S[I] of - '.': - begin - AddVersionChunk(Copy(S, Index, Count), A); - Count := 0; - Index := I + 1; - end; - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - begin - Count := Count + 1; - end; - else - RaiseException('Invalid char in version string'); - end; - end; - Result := AddVersionChunk(Copy(S, Index, Count), A); -end; - -function GetVersionValue(const A: TIntegerArray; Index, - Length: Integer): Integer; -begin - Result := 0; - if (Index >= 0) and (Index < Length) then - Result := A[Index]; -end; - -function CompareVersionStr(const A, B: string): TCompareResult; -var - I: Integer; - VerLenA, VerLenB: Integer; - VerIntA, VerIntB: TIntegerArray; -begin - Result := crEquals; - - VerLenA := ParseVersionStr(A, VerIntA); - VerLenB := ParseVersionStr(B, VerIntB); - - for I := 0 to Max(VerLenA, VerLenB) - 1 do - begin - Result := CompareValue(GetVersionValue(VerIntA, I, VerLenA), - GetVersionValue(VerIntB, I, VerLenB)); - if Result <> crEquals then - Exit; - end; -end; - -function DownloadFile(const URL: string; var Response: string): Boolean; -var - WinHttpRequest: Variant; -begin - Result := True; - try - WinHttpRequest := CreateOleObject('WinHttp.WinHttpRequest.5.1'); - WinHttpRequest.Open('GET', URL, False); - WinHttpRequest.Send; - Response := WinHttpRequest.ResponseText; - except - Result := False; - Response := GetExceptionMessage; - end; -end; - -function InitializeSetup: Boolean; -var - ErrorCode: Integer; - SetupVersion: string; - LatestVersion: string; - -begin - Result := True; - - //Check for Processor SSE2 support. - #if defined(sse2_required) - if not IsSSE2Supported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; - #elif defined(sse_required) - if not IsSSESupported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; - #endif - - if DownloadFile(VersionURL, LatestVersion) then - begin - SetupVersion := '{#SetupSetting('AppVersion')}'; - if CompareVersionStr(LatestVersion, SetupVersion) = crGreater then - begin - if MsgBox('There is a newer version of {#SetupSetting('AppName')} available. Do ' + - 'you want to visit the site?', mbConfirmation, MB_YESNO) = IDYES then - begin - Result := not ShellExec('', UpdateURL, '', '', SW_SHOW, ewNoWait, - ErrorCode); - end; - end; - end; - -end; - -// Check if Notepad3 has replaced Windows Notepad -function DefaulNotepadCheck(): Boolean; -var - sDebugger: String; -begin - if RegQueryStringValue(HKLM, IFEO, 'Debugger', sDebugger) and - (sDebugger = (ExpandConstant('"{app}\Notepad3.exe" /z'))) then begin - Log('Custom Code: {#app_name} is set as the default notepad'); - Result := True; - end - else begin - Log('Custom Code: {#app_name} is NOT set as the default notepad'); - Result := False; - end; -end; - -#if defined(sse_required) || defined(sse2_required) -function IsProcessorFeaturePresent(Feature: Integer): Boolean; -external 'IsProcessorFeaturePresent@kernel32.dll stdcall'; -#endif - -#if defined(sse_required) -function IsSSESupported(): Boolean; -begin - // PF_XMMI_INSTRUCTIONS_AVAILABLE - Result := IsProcessorFeaturePresent(6); -end; - -#elif defined(sse2_required) - -function IsSSE2Supported(): Boolean; -begin - // PF_XMMI64_INSTRUCTIONS_AVAILABLE - Result := IsProcessorFeaturePresent(10); -end; - -#endif - - -function IsOldBuildInstalled(sInfFile: String): Boolean; -begin - if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and - FileExists(ExpandConstant('{pf}\Notepad2\' + sInfFile)) then - Result := True - else - Result := False; -end; - - -function IsUpgrade(): Boolean; -var - sPrevPath: String; -begin - sPrevPath := WizardForm.PrevAppDir; - Result := (sPrevPath <> ''); -end; - - -// Check if Notepad3's settings exist -function SettingsExistCheck(): Boolean; -begin - if FileExists(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')) then begin - Log('Custom Code: Settings are present'); - Result := True; - end - else begin - Log('Custom Code: Settings are NOT present'); - Result := False; - end; -end; - - -function UninstallOldVersion(sInfFile: String): Integer; -var - iResultCode: Integer; -begin - // Return Values: - // 0 - no idea - // 1 - error executing the command - // 2 - successfully executed the command - - // default return value - Result := 0; - // TODO: use RegQueryStringValue - if not Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx ' + '"{pf}\Notepad2\' + sInfFile +'",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin - Result := 1; - end - else begin - Result := 2; - Sleep(200); - end; -end; - - -function ShouldSkipPage(PageID: Integer): Boolean; -begin - // Hide the license page if IsUpgrade() - if IsUpgrade() and (PageID = wpLicense) then - Result := True; -end; - - -procedure AddReg(); -begin - RegWriteStringValue(HKCR, 'Applications\notepad3.exe', 'AppUserModelID', 'Notepad3'); - RegWriteStringValue(HKCR, 'Applications\notepad3.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad3.exe" %1')); - RegWriteStringValue(HKCR, '*\OpenWithList\notepad3.exe', '', ''); -end; - - -procedure CleanUpSettings(); -begin - DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')); - DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\minipath.ini')); - RemoveDir(ExpandConstant('{userappdata}\Rizonesoft\Notepad3')); -end; - - -procedure RemoveReg(); -begin - RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad3.exe'); - RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad3.exe'); -end; - - -procedure CurPageChanged(CurPageID: Integer); -begin - if CurPageID = wpSelectTasks then - WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) - else if CurPageID = wpFinished then - WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish); -end; - - -procedure CurStepChanged(CurStep: TSetupStep); -begin - if CurStep = ssInstall then begin - if IsTaskSelected('reset_settings') then - CleanUpSettings(); - - if IsOldBuildInstalled('Uninstall.inf') or IsOldBuildInstalled('Notepad2.inf') then begin - if IsOldBuildInstalled('Uninstall.inf') then begin - Log('Custom Code: The old build is installed, will try to uninstall it'); - if UninstallOldVersion('Uninstall.inf') = 2 then - Log('Custom Code: The old build was successfully uninstalled') - else - Log('Custom Code: Something went wrong when uninstalling the old build'); - end; - - if IsOldBuildInstalled('Notepad2.inf') then begin - Log('Custom Code: The official Notepad2 build is installed, will try to uninstall it'); - if UninstallOldVersion('Notepad2.inf') = 2 then - Log('Custom Code: The official Notepad2 build was successfully uninstalled') - else - Log('Custom Code: Something went wrong when uninstalling the official Notepad2 build'); - end; - - // This is the case where the old build is installed; the DefaulNotepadCheck() returns true - // and the set_default task isn't selected - if not IsTaskSelected('remove_default') then - RegWriteStringValue(HKLM, IFEO, 'Debugger', ExpandConstant('"{app}\Notepad3.exe" /z')); - - end; - end; - - if CurStep = ssPostInstall then begin - if IsTaskSelected('set_default') then - RegWriteStringValue(HKLM, IFEO, 'Debugger', ExpandConstant('"{app}\Notepad3.exe" /z')); - if IsTaskSelected('remove_default') then begin - RegDeleteValue(HKLM, IFEO, 'Debugger'); - RegDeleteKeyIfEmpty(HKLM, IFEO); - end; - // Always add Notepad3's AppUserModelID and the rest registry values - AddReg(); - end; - -end; - - -procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); -begin - // When uninstalling, ask the user to delete Notepad3's settings - if CurUninstallStep = usUninstall then begin - if SettingsExistCheck() then begin - if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then - CleanUpSettings(); - end; - if DefaulNotepadCheck() then - RegDeleteValue(HKLM, IFEO, 'Debugger'); - RegDeleteKeyIfEmpty(HKLM, IFEO); - RemoveReg(); - end; -end; - - -procedure InitializeWizard(); -begin - WizardForm.SelectTasksLabel.Hide; - WizardForm.TasksList.Top := 0; - WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight; -end; diff --git a/Build/notepad3_setup_replace.iss.bak b/Build/notepad3_setup_replace.iss.bak deleted file mode 100644 index 2ebaa5abe..000000000 --- a/Build/notepad3_setup_replace.iss.bak +++ /dev/null @@ -1,549 +0,0 @@ -;* Notepad3 - Installer script -;* -;* Copyright (C) 2008-2016 Rizonesoft - -; Requirements: -; Inno Setup: http://www.jrsoftware.org/isdl.php - -; Preprocessor related stuff -#if VER < EncodeVer(5,5,9) - #error Update your Inno Setup version (5.5.9 or newer) -#endif - -#define bindir "..\Bin" - -#ifnexist bindir + "\Release_x86_v141\Notepad3.exe" - #error Compile Notepad3 x86 first -#endif - -#ifnexist bindir + "\Release_x86_v141\minipath.exe" - #error Compile MiniPath x86 first -#endif - -#ifnexist bindir + "\Release_x86_v141\np3encrypt.exe" - #error Compile np3encrypt.exe x86 first -#endif - -#ifnexist bindir + "\Release_x64_v141\Notepad3.exe" - #error Compile Notepad3 x64 first -#endif - -#ifnexist bindir + "\Release_x64_v141\minipath.exe" - #error Compile MiniPath x64 first -#endif - #ifnexist bindir + "\Release_x64_v141\np3encrypt.exe" - #error Compile np3encrypt.exe x64 first -#endif - -#define app_version GetFileVersion(bindir + "\Release_x86_v141\Notepad3.exe") -#define app_name "Notepad3" -#define app_copyright "Copyright © 2008-2016, Rizonesoft." -#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch" - -[Setup] -AppId={#app_name} -AppName={#app_name} -AppVersion={#app_version} -AppVerName={#app_name} {#app_version} -AppPublisher=Rizonesoft -AppPublisherURL=https://rizonesoft.com -AppSupportURL=https://rizonesoft.com -AppUpdatesURL=https://rizonesoft.com -AppContact=https://rizonesoft.com -AppCopyright={#app_copyright} -;VersionInfoVersion={#app_version} -UninstallDisplayIcon={app}\Notepad3.exe -UninstallDisplayName={#app_name} {#app_version} -DefaultDirName={pf}\Notepad3 -LicenseFile=License.txt -OutputDir=.\Packages -OutputBaseFilename={#app_name}_x_{#app_version} -SetupIconFile=.\Resources\Setup.ico -WizardImageFile=compiler:WizModernImage-IS.bmp -WizardSmallImageFile=.\Resources\WizardSmallImageFile.bmp -Compression=lzma2/max -InternalCompressLevel=max -SolidCompression=yes -EnableDirDoesntExistWarning=no -AllowNoIcons=yes -ShowTasksTreeLines=yes -DisableDirPage=yes -DisableProgramGroupPage=yes -DisableReadyPage=yes -DisableWelcomePage=yes -AllowCancelDuringInstall=no -MinVersion=5.1sp3 -ArchitecturesAllowed=x86 x64 -ArchitecturesInstallIn64BitMode=x64 -#ifexist "..\signinfo_notepad3.txt" -SignTool=MySignTool -#endif -CloseApplications=true -SetupMutex='{#app_name}' + '_setup_mutex' - - -[Languages] -Name: en; MessagesFile: compiler:Default.isl - - -[Messages] -;BeveledLabel ={#app_name} {#app_version} - Compiled with VC2015 -SetupAppTitle =Setup - {#app_name} -SetupWindowTitle =Setup - {#app_name} - - -[CustomMessages] -en.msg_AppIsRunning =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -en.msg_DeleteSettings =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them. -#if defined(sse_required) -en.msg_simd_sse =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities. -#elif defined(sse2_required) -en.msg_simd_sse2 =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities. -#endif -en.tsk_AllUsers =For all users -en.tsk_CurrentUser =For the current user only -en.tsk_Other =Other tasks: -en.tsk_ResetSettings =Reset {#app_name}'s settings -en.tsk_RemoveDefault =Restore Windows notepad -en.tsk_SetDefault =Replace Windows notepad with {#app_name} -en.tsk_StartMenuIcon =Create a Start Menu shortcut -en.tsk_LaunchWelcomePage =Visit Rizonesoft for more downloads - - -[Tasks] -Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked -Name: desktopicon\user; Description: {cm:tsk_CurrentUser}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive -Name: desktopicon\common; Description: {cm:tsk_AllUsers}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive -Name: startup_icon; Description: {cm:tsk_StartMenuIcon}; GroupDescription: {cm:AdditionalIcons} -Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 6.01 -Name: reset_settings; Description: {cm:tsk_ResetSettings}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: SettingsExistCheck() -Name: set_default; Description: {cm:tsk_SetDefault}; GroupDescription: {cm:tsk_Other}; Check: not DefaulNotepadCheck() -Name: remove_default; Description: {cm:tsk_RemoveDefault}; GroupDescription: {cm:tsk_Other}; Flags: checkedonce unchecked; Check: DefaulNotepadCheck() - - -[Files] -Source: {#bindir}\Release_x64_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\Notepad3.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() -Source: License.txt; DestDir: {app}; Flags: ignoreversion -Source: Readme.txt; DestDir: {app}; Flags: ignoreversion -Source: Notepad3.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall -Source: {#bindir}\Release_x64_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\minipath.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() -Source: minipath.ini; DestDir: {userappdata}\Rizonesoft\Notepad3; Flags: onlyifdoesntexist uninsneveruninstall -Source: {#bindir}\Release_x64_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: Is64BitInstallMode() -Source: {#bindir}\Release_x86_v141\np3encrypt.exe; DestDir: {app}; Flags: ignoreversion; Check: not Is64BitInstallMode() - -[Dirs] -Name: "{userappdata}\Rizonesoft\Notepad3\Favorites" - -[Icons] -Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {userdesktop}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: desktopicon\user; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {userstartmenu}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: startup_icon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad3; IconFilename: {app}\Notepad3.exe; IconIndex: 0 -Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad3.exe; Tasks: quicklaunchicon; Comment: {#app_name} {#app_version}; WorkingDir: {app}; IconFilename: {app}\Notepad3.exe; IconIndex: 0 - - -[INI] -Filename: {app}\Notepad3.ini; Section: Notepad3; Key: Notepad3.ini; String: %APPDATA%\Rizonesoft\Notepad3\Notepad3.ini -Filename: {app}\minipath.ini; Section: minipath; Key: minipath.ini; String: %APPDATA%\Rizonesoft\Notepad3\minipath.ini - -Filename: {userappdata}\Rizonesoft\Notepad3\Notepad3.ini; Section: Settings; Key: Favorites; String: %APPDATA%\Rizonesoft\Notepad3\Favorites\ - - -[Run] -Filename: {app}\Notepad3.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked -Filename: "http://www.rizonesoft.com/downloads/"; Description: {cm:tsk_LaunchWelcomePage}; Flags: nowait postinstall shellexec skipifsilent - - -[InstallDelete] -Type: files; Name: {userdesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\user') and IsUpgrade() -Type: files; Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade() -Type: files; Name: {userstartmenu}\{#app_name}.lnk; Check: not IsTaskSelected('startup_icon') and IsUpgrade() -Type: files; Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon') and IsUpgrade(); OnlyBelowVersion: 6.01 -Type: files; Name: {app}\Notepad3.ini -Type: files; Name: {app}\Readme.txt -Type: files; Name: {app}\minipath.ini - - -[UninstallDelete] -Type: files; Name: {app}\Notepad3.ini -Type: files; Name: {app}\minipath.ini -Type: dirifempty; Name: {app} - -[Code] -const - IFEO = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe'; - VersionURL = 'https://www.rizonesoft.com/update/Notepad3.rus'; - UpdateURL = 'https://www.rizonesoft.com/downloads/notepad3/update/'; - -type - TIntegerArray = array of Integer; - TCompareResult = ( - crLesser, - crEquals, - crGreater - ); - -function Max(A, B: Integer): Integer; -begin - if A > B then Result := A else Result := B; -end; - -function CompareValue(A, B: Integer): TCompareResult; -begin - if A = B then - Result := crEquals - else - if A < B then - Result := crLesser - else - Result := crGreater; -end; - -function AddVersionChunk(const S: string; var A: TIntegerArray): Integer; -var - Chunk: Integer; -begin - Chunk := StrToIntDef(S, -1); - if Chunk <> -1 then - begin - Result := GetArrayLength(A) + 1; - SetArrayLength(A, Result); - A[Result - 1] := Chunk; - end - else - RaiseException('Invalid format of version string'); -end; - -function ParseVersionStr(const S: string; var A: TIntegerArray): Integer; -var - I: Integer; - Count: Integer; - Index: Integer; -begin - Count := 0; - Index := 1; - - for I := 1 to Length(S) do - begin - case S[I] of - '.': - begin - AddVersionChunk(Copy(S, Index, Count), A); - Count := 0; - Index := I + 1; - end; - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - begin - Count := Count + 1; - end; - else - RaiseException('Invalid char in version string'); - end; - end; - Result := AddVersionChunk(Copy(S, Index, Count), A); -end; - -function GetVersionValue(const A: TIntegerArray; Index, - Length: Integer): Integer; -begin - Result := 0; - if (Index >= 0) and (Index < Length) then - Result := A[Index]; -end; - -function CompareVersionStr(const A, B: string): TCompareResult; -var - I: Integer; - VerLenA, VerLenB: Integer; - VerIntA, VerIntB: TIntegerArray; -begin - Result := crEquals; - - VerLenA := ParseVersionStr(A, VerIntA); - VerLenB := ParseVersionStr(B, VerIntB); - - for I := 0 to Max(VerLenA, VerLenB) - 1 do - begin - Result := CompareValue(GetVersionValue(VerIntA, I, VerLenA), - GetVersionValue(VerIntB, I, VerLenB)); - if Result <> crEquals then - Exit; - end; -end; - -function DownloadFile(const URL: string; var Response: string): Boolean; -var - WinHttpRequest: Variant; -begin - Result := True; - try - WinHttpRequest := CreateOleObject('WinHttp.WinHttpRequest.5.1'); - WinHttpRequest.Open('GET', URL, False); - WinHttpRequest.Send; - Response := WinHttpRequest.ResponseText; - except - Result := False; - Response := GetExceptionMessage; - end; -end; - -function InitializeSetup: Boolean; -var - ErrorCode: Integer; - SetupVersion: string; - LatestVersion: string; - -begin - Result := True; - - //Check for Processor SSE2 support. - #if defined(sse2_required) - if not IsSSE2Supported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; - #elif defined(sse_required) - if not IsSSESupported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; - #endif - - if DownloadFile(VersionURL, LatestVersion) then - begin - SetupVersion := '{#SetupSetting('AppVersion')}'; - if CompareVersionStr(LatestVersion, SetupVersion) = crGreater then - begin - if MsgBox('There is a newer version of {#SetupSetting('AppName')} available. Do ' + - 'you want to visit the site?', mbConfirmation, MB_YESNO) = IDYES then - begin - Result := not ShellExec('', UpdateURL, '', '', SW_SHOW, ewNoWait, - ErrorCode); - end; - end; - end; - -end; - -// Check if Notepad3 has replaced Windows Notepad -function DefaulNotepadCheck(): Boolean; -var - sDebugger: String; -begin - if RegQueryStringValue(HKLM, IFEO, 'Debugger', sDebugger) and - (sDebugger = (ExpandConstant('"{app}\Notepad3.exe" /z'))) then begin - Log('Custom Code: {#app_name} is set as the default notepad'); - Result := True; - end - else begin - Log('Custom Code: {#app_name} is NOT set as the default notepad'); - Result := False; - end; -end; - -#if defined(sse_required) || defined(sse2_required) -function IsProcessorFeaturePresent(Feature: Integer): Boolean; -external 'IsProcessorFeaturePresent@kernel32.dll stdcall'; -#endif - -#if defined(sse_required) -function IsSSESupported(): Boolean; -begin - // PF_XMMI_INSTRUCTIONS_AVAILABLE - Result := IsProcessorFeaturePresent(6); -end; - -#elif defined(sse2_required) - -function IsSSE2Supported(): Boolean; -begin - // PF_XMMI64_INSTRUCTIONS_AVAILABLE - Result := IsProcessorFeaturePresent(10); -end; - -#endif - - -function IsOldBuildInstalled(sInfFile: String): Boolean; -begin - if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and - FileExists(ExpandConstant('{pf}\Notepad2\' + sInfFile)) then - Result := True - else - Result := False; -end; - - -function IsUpgrade(): Boolean; -var - sPrevPath: String; -begin - sPrevPath := WizardForm.PrevAppDir; - Result := (sPrevPath <> ''); -end; - - -// Check if Notepad3's settings exist -function SettingsExistCheck(): Boolean; -begin - if FileExists(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')) then begin - Log('Custom Code: Settings are present'); - Result := True; - end - else begin - Log('Custom Code: Settings are NOT present'); - Result := False; - end; -end; - - -function UninstallOldVersion(sInfFile: String): Integer; -var - iResultCode: Integer; -begin - // Return Values: - // 0 - no idea - // 1 - error executing the command - // 2 - successfully executed the command - - // default return value - Result := 0; - // TODO: use RegQueryStringValue - if not Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx ' + '"{pf}\Notepad2\' + sInfFile +'",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin - Result := 1; - end - else begin - Result := 2; - Sleep(200); - end; -end; - - -function ShouldSkipPage(PageID: Integer): Boolean; -begin - // Hide the license page if IsUpgrade() - if IsUpgrade() and (PageID = wpLicense) then - Result := True; -end; - - -procedure AddReg(); -begin - RegWriteStringValue(HKCR, 'Applications\notepad3.exe', 'AppUserModelID', 'Notepad3'); - RegWriteStringValue(HKCR, 'Applications\notepad3.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad3.exe" %1')); - RegWriteStringValue(HKCR, '*\OpenWithList\notepad3.exe', '', ''); -end; - - -procedure CleanUpSettings(); -begin - DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\Notepad3.ini')); - DeleteFile(ExpandConstant('{userappdata}\Rizonesoft\Notepad3\minipath.ini')); - RemoveDir(ExpandConstant('{userappdata}\Rizonesoft\Notepad3')); -end; - - -procedure RemoveReg(); -begin - RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad3.exe'); - RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad3.exe'); -end; - - -procedure CurPageChanged(CurPageID: Integer); -begin - if CurPageID = wpSelectTasks then - WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) - else if CurPageID = wpFinished then - WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish); -end; - - -procedure CurStepChanged(CurStep: TSetupStep); -begin - if CurStep = ssInstall then begin - if IsTaskSelected('reset_settings') then - CleanUpSettings(); - - if IsOldBuildInstalled('Uninstall.inf') or IsOldBuildInstalled('Notepad2.inf') then begin - if IsOldBuildInstalled('Uninstall.inf') then begin - Log('Custom Code: The old build is installed, will try to uninstall it'); - if UninstallOldVersion('Uninstall.inf') = 2 then - Log('Custom Code: The old build was successfully uninstalled') - else - Log('Custom Code: Something went wrong when uninstalling the old build'); - end; - - if IsOldBuildInstalled('Notepad2.inf') then begin - Log('Custom Code: The official Notepad2 build is installed, will try to uninstall it'); - if UninstallOldVersion('Notepad2.inf') = 2 then - Log('Custom Code: The official Notepad2 build was successfully uninstalled') - else - Log('Custom Code: Something went wrong when uninstalling the official Notepad2 build'); - end; - - // This is the case where the old build is installed; the DefaulNotepadCheck() returns true - // and the set_default task isn't selected - if not IsTaskSelected('remove_default') then - RegWriteStringValue(HKLM, IFEO, 'Debugger', ExpandConstant('"{app}\Notepad3.exe" /z')); - - end; - end; - - if CurStep = ssPostInstall then begin - if IsTaskSelected('set_default') then - RegWriteStringValue(HKLM, IFEO, 'Debugger', ExpandConstant('"{app}\Notepad3.exe" /z')); - if IsTaskSelected('remove_default') then begin - RegDeleteValue(HKLM, IFEO, 'Debugger'); - RegDeleteKeyIfEmpty(HKLM, IFEO); - end; - // Always add Notepad3's AppUserModelID and the rest registry values - AddReg(); - end; - -end; - - -procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); -begin - // When uninstalling, ask the user to delete Notepad3's settings - if CurUninstallStep = usUninstall then begin - if SettingsExistCheck() then begin - if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then - CleanUpSettings(); - end; - if DefaulNotepadCheck() then - RegDeleteValue(HKLM, IFEO, 'Debugger'); - RegDeleteKeyIfEmpty(HKLM, IFEO); - RemoveReg(); - end; -end; - - -procedure InitializeWizard(); -begin - WizardForm.SelectTasksLabel.Hide; - WizardForm.TasksList.Top := 0; - WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight; -end; - - -function InitializeSetup(): Boolean; -begin - Result := True; - -#if defined(sse2_required) - if not IsSSE2Supported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; -#elif defined(sse_required) - if not IsSSESupported() then begin - SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK); - Result := False; - end; -#endif - -end; diff --git a/Readme.md b/Readme.md index 26aef9b90..588c4f691 100644 --- a/Readme.md +++ b/Readme.md @@ -8,6 +8,12 @@ Notepad3 is a fast and light-weight Scintilla-based text editor with syntax high > *Notepad3 is based on code from Florian Balmer's Notepad2 and XhmikosR's Notepad2-mod. MiniPath is based on code from Florian Balmer's metapath.* +## Important links! +* Notepad3 download page - https://www.rizonesoft.com/downloads/notepad3/ +* Latest changelog (release notes) - https://www.rizonesoft.com/downloads/notepad3/update/ +* Notepad3 Documentation - https://www.rizonesoft.com/documents/notepad3/ +* User Testimonials - https://www.rizonesoft.com/testimonials/ + ## Changes compared to Flo's official [Notepad2](http://www.flos-freeware.ch/notepad2.html) (made in [Notepad2-mod](https://xhmikosr.github.io/notepad2-mod/)): * Code folding diff --git a/Versions/build.txt b/Versions/build.txt index 227b54a06..9babd2833 100644 --- a/Versions/build.txt +++ b/Versions/build.txt @@ -1 +1 @@ -802 +828 diff --git a/res/Notepad3.exe.manifest.conf b/res/Notepad3.exe.manifest.conf index 067b6aea3..728bafc7c 100644 --- a/res/Notepad3.exe.manifest.conf +++ b/res/Notepad3.exe.manifest.conf @@ -3,7 +3,7 @@ Notepad3 diff --git a/src/VersionEx.h b/src/VersionEx.h index 96e0da613..5cc2f112e 100644 --- a/src/VersionEx.h +++ b/src/VersionEx.h @@ -5,7 +5,7 @@ // ////////////////////////////////////////////////////////// #define VERSION_MAJOR 3 #define VERSION_MINOR 18 -#define VERSION_REV 106 -#define VERSION_BUILD 808 +#define VERSION_REV 113 +#define VERSION_BUILD 828 #define SCINTILLA_VER 402 #define ONIGMO_REGEX_VER 6.1.3 diff --git a/themes/professional/Toolbar.bmp b/themes/professional/Toolbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1f7321339be857f718d4f1745854a31cb262696d GIT binary patch literal 102454 zcmeEPcU;fg|MzK6N<*^8<=W%A+?z2CjZdA-khpZ7WEd0yiTpVWU7uMzw8C;UoiD@vd( zkNZnIbUvMn!4!aUsvF}htMWx2?kj}G)goBin6t82)j=bj0n@iC#6~ZqB zmQJ5KplaJuO@0$uS)Qc0xHzRHOJH^WYmnpdcv7MwA{AxZn9`7vkYH&DzMHS5Ay1i; z5)=J5jk>;vA93CPO8e80c@q`kiBS8(-iIoo6+ul#LR`LgtL9=2DW|ry8dAQPs2ERF zR8*Ym9m@}Eli7EmPH0JBd22yXBd~r`o*+q}-nY?L+t*%yj}YnAr*Gq)J$p9q)2C1K zUcGuX>)yS)D*dm_K>JO*b?erYzCjf=H8oXvdHH6Nl9J8od6?|)mS0=>RXr{)-cv+W zw2cIhr$FDS1S^Kbz(e4F)D_y=Dn(nhU)(s8x1-ot=FGVfB=n+ z|Bj|-WY1k||JP9RKg(gVI5chARB7nYp)FKYdiSBHyY6h?x%19?mr>O+ z`;q@IUuwte-02r-$_K00tbW%As#Hgt^zPYH{6{|H|6({U6;OgoM zhIQ-Kaag-{Ef`!}Tq>|;%^C=>di81uU~FuBp2}2(_(!!qh}W(MkJqx32v5D^k45wL zB0SaB72o|LYb##o&Yjpd#`+Z2j|kfJAGu|6lv}oFAtk7joti20+A1lseoHXMWMj2{ zKLCtVwx<4BUP)HE#yTK)rvKm8je~UTXdjA|fR( zCDo?l8~>j2baZq)Thn(}_1k0Lq2SvSYz5z+U~6Jxa*66dGi7DvitkWFT3)1)s5oo? z7!PPdsOUdwUy<&KTD58wY67(xs0{*{!dqeaEmc*RO!nst<_Xn8iMbpK558TeJ95)bygiS(~3T8YouQF+>iMGf2I9t$h^_J5pa)IEfgA$ z-#lyLjlBjM&-4fOTQ4Oprrt(LLF!jE<%%}AA!W0)8cM#Dq%==jN`}VVA~dJKYe(aL z9qOBXX`bqwx_axx_U*f-(Kd5bl#kn zzE%F$%3fM}n23Z#M`_|7eI+FRUM?l2aZ*-x;X_$jyLWPOOG;?_p8ijz;}LXzorp+_ zk(BS|jT(tlnJExwYf`88+>rgBw80;xMj3H1&xz+?l64u4&4)swrP5G(wi^4thLle- zD*Ek~En5!Uym_p3-+~>w|3tJ^R+M4s^zZ+>gqWx(%^z^%UpAleqrt|%N;LkE6fO@M zBZzI8plYmcP`ncyNHOa0^{Ipev7G2 z*%+##52dlqK*nVn;#XCE&tE!dXpJ6`r#)&o>H^I%Bl6m+tIen9Yc4A#CH`yM*1T@* z+fLIOJ2G2))G$ip>zF%y$eZ@+&8=A)EdK&T*_$;vZmC^#&BGoit@Hu~$A!0%%S=^? zo9Lgi(hCT)(7JofY~DKp2*re_Cl~4-J!@lV9_3)JCVXFQ`~M5=Pi@ckEyvl|pXN50 zo}iUlW4Q^`&%L)UMYrs?X*w4N3>~$YxW`vv41P|AX*yfcCU*9JP2zm*hz^8C_I|n`U4FdULq*9z*SNS!SrJUR}o~-O265`^0 zrpd?{Us6$7&HwGUP>dgc5p(Az!_e>rOii=6t)^xQCQP`5e*N}CRn-MDGDf$=#JZc1 zd>Us+lD<;;iyW+>`#)KufzVDnhv^SaV|vPI=sY`*#+JjeaW3lvtM`8mEnm=YhZt$% ztg93DZ1u*3aW>G=y$^k(7cex=z?`{{&{TQb{+i1Ef8m&~*CtUf4>yz*zWHVle*w+i zx9HcmZ>@FzTk|R}KanRX*-KnRL`7OeL|KyXQ|D5@BwyNSU`b8&PkqJATKUJu_3Om8 zZ}UQaem-8me$8#!|70uELQSn%jpMYd_QuE!-;Ug~e)R+0!|tW;Q^9G4<7DfF3o;j3 zEyT~l!ugi32md{21Ibqse*d1Hp5lyul$4a9xVRX_A3oqiQ4ygKMTG?@DkwnV`}c%* zD0uf4@87<``!}!g{&hayz0SkC{9L@v%fTCl-0aWD;a_Z zNm~1s5b(1gwb1*u*pL}3q}%)|wb5-oJ;u-dm6erY^{SPntbHcQjRtpaC$6s0xZj3< z#%5giazOrBUxXNF;Ofx>cyKAUPIz?fGIrTm5-hNDu{nYlnIVY5+7x?@W^-w>baPL5 zW9ytrOm=J1e^k7O0c~6IdbUs#>DpXXq>GvghpuX>BDJ9#UDr*$nMil_?;BgQ{C_V7 zc4)&RyAJNzYkOWnp4gV&VS|~-N*)zd;ikydr3%l>OWs`tVycU6e~Uf!cd)R z#eKUZyn*DeXZ(kguQy>#);_m&IJ|BpE`$W*#;N1Dbo3A+b_L@A^2UzD%W;$GSX6gf9)iqJ%04#J(R}rjcak`;67ZZG~x~&!0Djv*tc;5 zd>riHYH5s>dYW7scC)8r&X7TQltx?O_kYI5K=0Dp#mJ5b!s$i&rBU{#y#?nZml}^c zF<<-C$%Q&4mtAd<6tou4k9s4AuFa3xh1{qe$T;bThr8Vn@3sVVUGXVvz1`8v%__+d zq$k+^FSI{`XZhOJ-rk^V$4OU}H4%}O9o$J; z+913|3wMrlFs@-@k_m0?bj+TeiWxH=V%oH1{PWKP?td+I7qF0v!VMx8Pg%qBK|QUAq?a19fNjy4CEJ%Li(w#HTHiEEuZzjyO%7) zX>U*L4G4t!d~1a6-AnIkJ63tUgo9@`rW+@sS@WF%f-?VK+iv&v;<>MLLCO2q-;7N( zwo+A5Y4!VWeQT`u-r8Ra+NzK?r_JD1O;>!~U`6AE#R(VzL`TI=@apdLKG6;C?LL3 zKz!pp@r`$H-Vo2o$J_io;uyJjO&sHOP8RaBGm)3|3VE3?k^AZeax$JH=jAhGzeq0! z?H9a#&Dn)eo-6SWg(ZPVd0&P{h4^WRdHxnM*1m7W6jkIs+&45>!E<9n8ly3OQCeEc zfpIxjo(vy9e&oQ;yR2TtI6zBQ{!{vN`)%XQ@vm-dU5>odKFHp^5oE7`g!7SjcsT~Q zqE2Jk5<4ulvx6hmO?Ip*G9Dyizk@aQ+goGbGAo2ES%4kHMHmMOHJXF0U^is%a!2-| z&G4QxnaOTLz9tfWdNX+Q{_HD3{6~(CF}wMywl~6dIKul)WF;izM|Ax~(st~S25n!2 z>*Uw=*K14^>!_;4=3DFh-0WIe3Gv1qRFtH@mj0K{_w5@rX6Vpk(9(K>F=Ni4OP7s@ z`}Xy57%<=v@sBk8^;algTwJBIj7;b6Y8Po72_VC824bj zgXRes=U{xJh`za^H~A=}xq-s`925{f5+`A{l%)IkRiOV+7&KE`eeMeUxgiq6eQyzN zVz|!@4D-H*p<6Cvs7Fk7{_!31nH?FE&-jnw_|b)D1AK9G!|FQmkLB94p*?QYCwhk+ z73Jlm2lwqQVls8&r?UZmb><%qS{l$CJg}I2K-vhu-xCY9A|HmhaeN{v$b~qBc1*N` z>3|apv@SA$MdQSdXlO^nD%Mgxpg;$cx@VW9=Z^ z*|w6UQ53PnaG|gq;r;)G_DA@6YTGCKb>6I5vqUW{EF_GKjKn8Qm>@D>z<>%KLi&3i z`O`2Tp%zV~C5H#-PEJnnS;3Vh>+iBoZo-?mUC5!bi?q=xbkP`7&GxM}+3fhwlHa~X zM=>cW>Hfd8>+~pK=~3J#zHyUqA?O1Rt^WisyHcz$=flc`4^tDsWIi9}_I%iRlw$q9 zPY8@x($y@K7=15ExivbUZ~`jFPNl_MXLFSgaqn%RlS%g>l1FYk~yc<>oc$6;Xb64R%r zP#bsWtC%(|1^W6gx%-9=ji7Op!+TND?q-yK6&|l#-zuDMw*S5T-`xulcy#|3b~-G? zE_*A;SPZYuKNgKeQt&!F-R}Vp&B>(_otk%L{a-`KXMM47|9fDCEh7EA5VSoQf15bK zi^fzBQXb&a#f$LR6bOsu$!OQ{=<~k@t@+~IMAdTong1`dUCeVKyO57>^Eey{-Gf7W zcYhTpj~uMHXM@XXC^c>Ri-JNEDdOQ@tao+In~I7q)z87L<>f7|O`LccpG_Qu;~yEsKZt{55C?fF#6ha?j}|RjRLp%T+wa7K0wCad z*-t_6i!vm=D}%gk0AE76xk5wr|8?OWoKA`KN{oN3TjR{}k5$t~i+F2JPQT~x#BmSC z6Ec0)5oef<`{$$Z`054RiHU%XjSbDQla6eWHFu017e?lzWE@>-izC$b9dcaEaS_Hr zf@v=1fXQ4g%}oFG$Ue9k+vfhuKaQAdhaEAU_nzUf$-DyEmL4^ohq{2S>o4NK1f6^-VZeYrWaFELAwy0;N~--x1%*GqtLQQy$8)1e0cYU;~<4^h=07!++I^#yMZ>OWOLKErHfAklz zoH?~5Jh1NkV+qrt4E*C6rNQxk%2uQ3stWl;H*VIfnKZ>H5R;XZ5F`XN;l2bu35*2?kF#g75k(l`#{ckV8HqV8ZtNF*#51!Lu$ za5&6959`@cFr6C#ea%Q{&5MSXUJSI%;-IxC9$JpKq2rza+mIA&ig*aWOKI2`c?*61 z8eB-_Xhvv9zL)a!-o(haq}rH9P0fj?pkOeoX;YU^GiKc5I0Vyk)P;9cy@u&Un9f8) zBN0kU>q^Ls&6aTx5fQaY*lK&k2KiuD&@Obb*Z`w@Z($jB1s7st@$kVzgzVY{%^BPH`}ap%j2myn z+20H5`hU4qYt~FeNvWx*jEsy}3w3p|?p?b|IWAkmFU-qAVP5t(!Fk1Un!lGHMDM*h z)p@R+V7|xX<18X0EnThteJ%egDq5tM`>R{iruL~bXWoO3&U5JMJ|`XA9VjZ=JtZ9* z$r4c{4pOb1{aV^}IbW0f&I=r>)hkvX>sbF1h2;kNdME4*@W(C!+miiNg6$%!TFdF0 zvlfbOpTei)n0kTT!|pX2)lXunoeeTGGnp>rr-12m*pJC2f4NHhoAD1XFRu#z!So%M z4(;b~@lYr(l5IoWfxYF}zXumWLpa3l+lAP@3_Edt&kn?dFa&e|b9#`^{Db+%Qa)P= z^AAhpMuR#OL6~QDVi(|=1ZxzP}t=0{dVF;h^bUBwjd&^t;!Q6d#8bD^`$w&l1?#+H%L(abe`7r6SB} z36440aUA5p@?po{GA0M6!w*`2U41G6Xj8uB6n z@PKSk4w=k-xoP&K3O*_PJ^jf2FiUN*FmtUJ>>1dzeC>JJd2T#Jd1U!yd1iV4)@SV3 zZ%2o}|2|B*k973!e}K4rry;Fc>4Vq5{SIWKxC1g zgX0~Hdk7(Wzbjs!@k2q}K76=w93_cSC`~z!QtA^v-aU(gt4EMa`jAU&7vhYK_Nmxa z7M1!&;r55=N@AK=G~GY}cP~Ez!rG`_>qbV|tIbal29S;5m?EaG)+^sSjYY3$Qv&9ONwVlFMcAeo&0P&x#O`l7Zib4lg7< z$TTV^RSG0ksSdDwGx<8ML1BTnGa$@CocEuD&b|xiwO}V)46ee(^d6R(UdKYy%QU{f z2osAq7%z&0p~Ge9lRcK6=S^t)--F@)M68KUg7?*j@Vs~zzYQK*M18*&y|4E4-WrpC zi8!Isyoj3Gs_{*muKhT7ZVHTyUXdLYwFh<3dDM0dyWi0873qQ>5eIQCAw3BBgs}0U zdWGEY?f(?*Cd1(Q`3hfifzP>F_7zM?I)llHr@{Qs1;+(jc1+f8624cypbuvK?+KUX zxVXcQZ1Y0U#nO%A9@FoaVbWdVq^B;!*V`ML+}#o3=T%DciGwI$^Wl*=ZURd(_)pNYfddcZM4utH&*ARAaKX*O5$9{<|`2xvk zP+H8Nu;u4;m8iX-s@PbPY_lrrLDlD~`hT-#`l8a(Lq|1lz9C;r>k(&PAgFiD)`0o+ zH*2;kM^v=a43eWHBBDq>SM`sucuXTX4}?^VHtO8a#WltNAbZ2a{Wse-QT&-@0`x$3NIuxrlTi)$Fi19mtpM zuyU)|VKI9wW`9)^|7h;C7m36_oF07oDOmmR6Yjk&MN@}e9RJv|$%9^R`H%JEEZ+=u z{ne-M7o^SjhnwqKj(=>^o+|FIP5k5V7G(Onmh%MSA4knKkr)?)SC5jAdhZrDW@Isj zR<2yh9b?CZkxylOm~HDS=Bg(`O@)F6Ah807Mh&i#9_iv5tAYPMMrk& zP;FfPr8LegGWeBzfM2m^VPin{Y_&bN;5f@8%PY$>`3kewT<>RR_w?-Lr!j8)d1z`q zMW4RGWfXI_C66bm)VA%iYkK-mF=^U8v~TZpm)b%(QBj%3l9DaEh>10AU*0xK%SuYN z;PNhzudA!eV$!ibpjsR@8Z&ex8!Yy$*!b7lqv ztZ)DYTB$$(P*jkfIAoCM674x9kvjtFY=31neG-Os>k>v~&BcG9LTz+r@x0h4`!^!z zj1Mvo(sy{o9XaIJ$NUyD53Mgh#*TkR?kR5+#vj6``;quSJR>t=7aoOfLPEeA-0@w7 zyME3{-njv3N46pNd??;sIfTouRv{GDBF#$HKZo|s;CgyLt2{XT2Fx+(;x`z^= zdnO4hE+xV1Rx(^;E}>J`ZqMnvA3^??ZMt;n!Z=7p{KZzSHn)c6}Q8T)DYUipGP_%+#lTnP5X?j3v3W&TFu z9`7(cu?&+F_;7p*%siF=69=+CxDt*~TeDIcd$nuZqUC?iKTM4bI%>@LCsu3joD%K1 zb0EO9DU)GI28LPbsmOl*^qa6~!F-GuJ{;4gOu}4^Ss)pN*+rvAjtC>a>h{Dx>Xd&K z6%De995t_9JMSWW{b!gp>v6SusG*Tcc7N$;(`HMNm{=cX@24sv(wKCO)#v(z+nc)V ze@VXd;_s_}!t!U$_(x>mUw_Ke+zxT6W=iT5^S(>xPL;+9!q*G$?^~+N3tlvj7i6vZ zMFcLK&GR;$Rx!?UShl3z{DbN)iLU*3@7}#ybtyE);cjGdrK;#Zhnk7zi3lJ_9i(rc7H=QD35~H;FyJGX4oRV?o>wg9={)1pw)r+)q_BtX@Qha z|HEc;9&~D^)U~~8Qz_<0UY|6{FSzS*%XzQZGqGo5&-k@`aP!-gXO{Q+$P&J`M^C@$ zBgRBP*XTLfSskVE{20fUEiGfFPQ3yxqcr?6_%O}+O|qp}yj!|=_sSeN@Bju32q~lZ zyfKoJt*8%{pxC@^E7}$slP@H{2tV5T(RaduVov!{o%AQp8O-pCjQMS3o$*HIDe~#71=(l4QE=@ragNi-2oJ*bO-o3xp;;Kd zL^~>Gg|XYkwF~AG3<$;-R$JOfI~Z&~vsm*coiDlSVM}&cb4$4-6>VQq5-<0qfRuo1Bp=LGZ5Y|E}2HMab^u@lc=_{g}5 z>zE$I#3T!Y2S<`Us_6yFe=`x0Hk=*S_x68^jvCNSzW}r6H(~Mo_GjFJUfNa6Nxj5D z=V?40zg2PGoc0Yfu0Y0dCcalb>xc8n&$WPj>~@mg<5w$Bj(hx*T!tx$e5^!|v5v{zA( z8_=PJl=<-AMI9ZM)ti6NbJeJq7wGEgy?&K{Fu7#v(N;MBH0BoHwryJl|6u+emku8& z2WN*xHdw-TSo_Hqt0p_FJ>_;-vHNzH`=Zm_Kmoo$F{9os0hI6daSLjk(QQ*Cr_Th(vmtK`U%Rkef#ztClmbt zEqy=dPeWXe@ej|9ZXEyc)txFHs9T+XoLZ=jgiEn_la+xCiiyDZ5YtUDuwzy6p2h{i zwhItsJ0F1*2g~2u9Nr5|u*JdvXK0+w*>!#P|JbhcFaIE$_d4;9oC6#1hHO$E1+FH2 zie}ahlUY5fc(}f+uG_6TQ+ONZPWYAXPrI>c8R>ZDJUMQu`IznRF`V&?)&=O_L`F)s zN6TgvHDQLkQp?`O@rjivUvSmW{PK?`{uGZdFf}!Gw;(Pyt9NCj^SB!o&e>qq)eejE zMJF93^HFyr4lB@qbhF&dQ=YlB#3Awp*?A;mGR>b(BLDM=+pb}P7y0*j(s;x5bOrxt z2>HVN!*!v#&K{5TczN#z-aJkOzu*lDiA%qrcnY^KMC0uK-PpZ(1GYLXg{S=@4!RS^ zvUcPb)uxKl|BN0X>SUygm-lbuL1Y*{WTd0?{TsZcI2cb8?%-63TtUty{I^DJv;SQM~?6 zG)EbJ=s+l5P+XMPd3m^h?+(tKI*E{-I}jYOo!g$J{}b=s#hd&*ynOx)M-Cl?-C}FL zoy}q#aWuqjFKgsdIekh#hlf6kkhH~;M)0+_7yqUF5x))VpfO_Bhr&G+CvJNIO6Qm2 z6X`w*$=)iTxW~)2z++NB-t`5p1Ori|>o^`tx{w1vDCs=*kiFH;3xNN1V9R}AeJa^z zWe^9+BR$A_;vpZ%x3P%)(y8qqr8$?zMiOI5zf_?E5tjeJKLhp$E?~~-yO{f&j|oqK zzbH+{LFRtuAbpn|z>eU2goc(PL6we-FkfCJ^U%_~;E? z2IS^`fTqs5Y8+(5$V-?qJ%akqcieTaG7B+lPE;ihBJc-MSKkIv(H`2gMj@vI`QH9d zVU;FqGVUVieKvv$$`MHWD{>w~pZJI#agF8K4-xP_tKvMnZe{L6$gG-!@0I^H`xV9Y z$VJSpMEtsF3yc!pVeaEHOikuvWjb+>6QoD7B%95slgK^b&&7Wc^uH9R;lJk}x;lkt zjvqr@bQCV1KSzk+;Iw=>#*G<`t?sVy@t_!jLh$u;ga77@U&Z8!<6&ZGh?wxRxOk4z zk2%N5Iez#MaU1O%8mCtB!KTVnX*|)i>16U5(emlvKMcmknV2#qse;oA^A6#Cb`6Wu z*00|Y8pBWAPCn_a$n`{)$E*LE*L9IEWk2`3>Ys3)y*%8{>|%3npRv+mSuFK)DzpFy z$JK?eW%~`W{>Ms3hf4fo=gyt@`|rPN_(z2f zq^OD=R)sJ6m+Y{}7yYvx7K@2h8~5w#ir_xNL$bFx|N(g#7#*f?vtnygAEFM->J1FLl&V1C_x}p{QWBwMN@+|w3+Z@sDQ2KW2^@CA!L3 zuU`DaLSs5;-9t@Ucb(%6;kI*brXSwK#S(c(aYSlD;kASKaOVV44{k)nlDY5V)|eV! z-MFY;-#|_ENYMV!xq9h8I37st2#d2v<4I7gRj%D8y?w=;iD0gTZ7CKk*Y;Fxe+19e zzFk`h;vc=1EMAm)`_@h5QB08wG11t%Y2$~}$M&T?x*wnLCM)$$(d!rY-{zzzrzYP{ z4L@->!+WzwaU6|V*!6es+@`p&s}Q;;1SeOU;J&X79{DZCO*bR5So-pJ_Fu`L^7lku z_fFlm#JWVFXiq*$?B9WJN~URyH>4uZBwfc-;vI>HK>k!f>=_^;95{0h zI7vE>qj!LP56Cu)#++VR#6f7tzM_b1u|Dx(LGQ_-q!@dO-of?C1^V_?_fsFv)-V^Z zNxq3gBxc^shHEyUPduhZ4zm0pLXW-W zGik0r1B=NA)0g^@mE<2r_E%w~1L;9NXU*7qk(Xb0Y$WL`^eN7jU@SL!%qccIN8RH`TvYnu&sZQ#&fQCpfUu`XB6nYP(yI z9afF}qaoJ+Shdoz68}g}PUgPiYuB!k59@4@58!w4jjCKlz(1JW>ilDyxUa8oCH_&t zL1^x#dOR%RA64UFkiPUhz399TKj^!py}@t$HCGq5zgw`}55S+w-mpDG@#U!8wM zEY>6Yk-bQ{9?#(cZNG$+Yggbk-9FKh7-D zB3}q<8$(Gyymw8lVD{V@Cw&pVM6bl~@4ikFv|i>f&6-MfRj=HhQJGb=r!#-=$}=3= zPjvR5J(bt~Gr}j_POtRo!A)H3()hKO2r!st6>g_%!S-1^Q+AGBvt-<$;HI+D?GN|(>ELN=g{+5(c>5%!zWhUm=|7xJ^of7mLP{O=A141yhH{f8L!>1o zWnx$9{~EJGhx3%eUO}l=TD!&&)cgs>eT+nMOg{4W%&uQ{QSgA{CBB0`2{Z% z`9)ce_(j=|_=TB?pWZ&dc|IcSslU(GvKMJj@aRDb#r9hbdpn!5by_3CH)sxByuo0? z7v~ZFOZf)lwI#>=H8QVoPbNNiXQ9kA2Ykyn;4diz-}w{DJoxxX+~fToAml1L}=1UT`MeAG$j5ky1rEg#7itCSCivJx!he}H+}J9ro8V{cI| z296*5p6suFCH^5@ll*+ET#R%+56io`aL)map=;tGdH8MRVVsOBsE~jB1s|a~q-%Ly z0?+rwSY1$rCG^c(yvu>{n+$klrz7yy6Z|!HbOG_z-^mZXnYy~4YU9Qp#hh)+%m)}d z{w{~nw7Gg^mB@)zPEY%NR?L7CUKt*`u16De+F1#UosVoTLj|5Qw;cWCKs(xiir zoqQYbMU`|vA;AG+dv^xm)$??$SnhyqvKeCkv+??mT>pdCp{v9{s7(UJHbi7(BsWe? zPfy3<#f!Q5E2@*<%|!(KgUPMRKS=i>&gRzz{vXW#>hh6;gwHlu%oa;vhefv474fiY zvBQd^_*i+F85|h@U~zXS53O3ZY$+~y_rGqUI!X3dx_3){3JgdN<2&u#PVi;aS}O$j zGk4JPAM4v0-(slgS6kmN;1z6r4;J4_kOtGKFwVy29N9JRNN@C(%9QHd1`z)^ycxmz|MHKsHpD-|{gHW;{3ec& z-k>(nc@`V!)hR!mb2Ns_^kHFbl$!LSwTzpzRFs$M)vB3z&z7oU{n|Dc8~SS-@xOj) zB{{HD>*i*|ezQL|UnA?fryUndC^up|ULJBIJ9y2aLyI(<$1FEgq5VR3jGbfghS;@7 zENA5y4({WuBq!aGWHfHow6S#e7AoRBNJif_YGOk=w-FoOt-ZvgKAk(b%^GC@Uy==Nr&P3Qx8#nLR5LX-^$`+^ioImi)bvAU&AtOerxz5j*E+go>sj1) zz~Vkod@Dba&v6h=|M7|GKi+T8di5$WIVmxOabvdLAh+fqtvAT*t~eX4I@)1z>kt+Y z|0pg3TT7kB|D6dE-L1TNDsxtptT^`yMh72bR!{GR8mNr9G?@|1g+59wH*VuJU9*Jo>Kz;`QMl9?Ps@JZ)kb zE$sY@ENS;+R%oxh>$4R3=Xc}HrG0pFxjJ6caTY)Aj<+4ouhj9Xt)6_#apCr-KK>s} z@4>+C5$Hcy`$KcQ-dt6udQ|J&h1-x`uG91db)p1yngo9r_3ucWmy+u z_}Q1w^YiXp=D&M-kGMxFieJCL$Ja0UA9B<9g&E2G_s?!TNxF8AbS0nCQXeAW?j1J2 zd7HkKZiJ?PcWv`Ud&;`WzjcxS=HX3#;mZ_!$V+F>&=B%xkDNZ--N>1AAJ6b<`BU&s z(!sZW1^)6p@YmB^(KgbB>@GveQ9gagI3J1B7 zhu>EpMkK}gttOx9hI{D;tSo(p#h-Fu_L1Td6{cb5>vR~ouVMN!WBUH7Y1=lhYuCdt zCYzNBQxh@8zhEO{KWkS&|}GG z?(vMcM<&G^pyJX$M|MJs*5h_)Fs^K{$MsbU@ycf{Ua|iDpeF*U|D}25_Nufl2K)XP znl!OLBP+WUKNw}w(%L5}J}swrXL7A9EpYS7Wh9aA@9Q#7G9b%g?xDQUtj{&nQ@cveG^!HW;(q|lWq_PvAn`K2!+2UTddmsDH=k) zxQkwm^-r=#=f1Vc-@kjOXee>jx3s1nv#(}b_W$fzGh~PSJ%}f$C&Jr?SpS38p{v9{ zsJ*V>7^EKsv-^HSad6qVRYOCg;+*hx_1PEj4<@%N|DZX4aese*;vXMr3{C5vb0{B= z3w)<5#lxzj9adge2DeT+@ej`CNSJ?g^NK(M%@^5IJN`4U{4v?_65r|Qd73ZNytP6w z#aU)Q#x0otIa_0cp{oCRZQNtks#O*Du-L_4;~wl?vY$Ws#nO9g8E80Fe3udNk0TVT zGLYg7=g3QJknTUNBvig0+FK;k@YuY`5fK+MH_?lYE>9z2jEW(!Y#USGR~{HdzZ+ zWn-*%B-;P7$hd3oRb2in@ekr4C1iVb>+C7Nt0%*@lMi*^@!&v?f0UNeTIVGnISx`? z2U0gXEVf3vF#qUb?ZZ>ibSha%{KMpMDrWCUg@LPW;1!;#bjncY|pBMnMbO$*ndusI&gVjp8y8|HxwR zMP61$B5IjNLdGdy6emQGuR=6FB%Gx+Do)`2^&`YZLMxDeVGkFx`JAKX$IGrJUB6au z>Uv(#{;-PprC)9BkMP>2b(#C9+Ww3mJ4S@;DP|lwcmNscPvPUWsr25Ju-hMBM#Yq6 zoQ~nYiXi@R@dCf_#S?sbpO4ZH?@{)t2&Eqj@Tnk|;{k;kDg1Zo90&3D-cria2|IEa z{eJ7an98}ejFe=>IE(UF>3ge_{H)mX{G6-AKay|o3tl{6+=r9h5b_6&T7GWLAru8Y zLaD_)@Xg2u(B?7ttDb}Jkqv$T=|T<^fq#~`|0O<(?*Q+}&MG?%AQyb3XOeHoYapSJ zILId;s*I1L{1WWs7r~4F1}^*@Eaktzd_MUUmOVsJ(NnCw5XR!$cv0CXj{k!Ee9u=H z<@$9F(w}@N&t(z^`I7vyR9KcgKw!ZWxST)A{Lr`3nuJOU3f?}$hFyfVUOFbuyk809 zrd`Ll>ETF7%>Po=tadK_`Fl*)jKbJyS1YB*(x0cBMmAaTWSeh%lzc$senkH#!|A3x_2Ov(OA#={h;fv(m1zxvA0-o1rl*+t{wojX_@5)Q|Q1<)t%VVXtUgLGd8 z=aVsU=_X{6uX)aXKV+=7knTcFl6DQuF@vg;r(t)%g^KRyoUTsX@Q1Zps8P-_;q?If$eo21l?i~q-_Ju%z zy*q*umM^ud_MCz?v?132Sm#11F8$;oZd$ygKPc zdWSjh&N~~o6nsaqtBjh{vA66TyM|r+nx2W>%Ve;$Seh(tCWFahGMQ|4zFbCdz4E~F z%HdcXxAR`qYlJ z-iSoc#eO`MdCnzHF_|zu@&t2tC1K94M9dCOz^s59%hlao_}$)~tpOt0Kg z5C_l6Snu-vxN|6YnN}zMVL59W^?l$<&1O7XGaLvm($h; zY9|)xX`NnVI3RMV8D~=`eEv)Og7$~5sk8qF+3$1L3b=w~fJbxEL6MC*1`_Vede-%j_DDDD3 z|L!&Z`?Mq!=epj5~&7^9Wpm7ruX3-pBQr$Snvls6%Lnj*JXI-zB7E514Hx)Bx zCDYhrO(IW3xyg^{|0GG*;gtKB{LOPXxV(M}=}i_$4W{|GD*a!*c!0c$k3s z8@40(K{hs%z1y-}TEC2nhvOd0HsRH|lXw+M_QXDG@W5*o5;v{HJx@p6+q@Eq!E3Qi zXJRQSp88V%qRiR`B_%gWIl0x)(Rs$LGtYde8yd|2f8M;Ov>u03G1WT-QPJ}CCzsjU z;=%m{JWqX$muXMQc0L=AuY}@^>qwk(8S>pYyI~B{Z;|gkjd5A}&q&UF(z7nLv8lvA zI(FPnvG<$N}$HS=-B$qZLkmLqdq{U}ifBhYb1H&L^Pn*+^i0_~b`5terC(ne!p~QApUV^FjU8v{ zvgc!YulgM9e_9(@gvLwKG|uZsanRzz&z|M{YnhL2Hu*9!8_l#wDTpLLIzOLnxPS8+ z;$!16p-QAyv=h4Y^ggNN~# zX=qqVAphAwRZpxe;c7FW(c8c06VGZO!3PyC$g12$M&hF&=phN4WGbFM@DE9<#4 zG2-{WuXwyhga7E!)sN#J2{-HH|FMo@Gt3w< z&F5EYo{zSlnSXJuKCR4fCzQkl)$m-=D$t3%P-1IBV9}$$A7+4{-EFgyigc)9Uoodz&AVt{`@HLm(l*(>)>xq06*jr z`1D=iLN*y%k%85dV(_!*`=T|gsGCG#32_kWqto#Ti73Psyv7-1B9xyFKmG%3;NOKK z{~E0M7h%jl2bYgmNC!fGVcojkVs)@C^7F&eG1@Z*7I$)L^#@@brD6D(bRbV@?Xk_d zA|lP2{D}TivLn{Tu?6~wcUwkrsh1-m$OXQ76Il$hUTxd7QDEPI zlJaIrS=sf_(R+qTQ}2`CKH0!EFeXn+gqC(1jrm+S{vjgLhsR{EbZ|iW<3|*y^d-#| zXQGI777=cvaWC8ycg`ArH}0M`Ld@2gD5SoJrO)I%Cd0)Q_V$(dM{{+rX}x)NHpc9UH%jBj24!)-m;`wQtmnEwSE!!liZP*4zh^ytwb zyn}HMVgAA7lKFF6;XIJ-m3UxaAjdz7Xs(oTkngp_%FiKRbV32uKeGF%#y|RQzkp== ze%+sx{S2R-)Bbr;~4Re-ELePdBxd`dk3XtLtIAse6}qurFl2fxv((@s~bf$rYtNV ze|9QYK@5O56a%X&-oDPq4F}>MJBWW!`iy_jwdGeA?9v<(R9^^@K0v%h!?0Qt&cUZV+(6F5) z3vF&-@sit^Ip-95^xT~C$H2fDRpqh&8ULVq#QB~x4pKt8k6YoVIs2>F!v_LS>9Z* zl`B^=`}3Bx1{&A4uD7yS9~rtEA1Ll_UHAvBH;8G&{zhY2S%VqFha{5!2r?4y;L-Ur zUyZ-JcA+W85B$SNf_#dUnl!Gl9|({;=ORQLzsK8`6}X*T)*)p{Rf-- zsM`Km&$n24gE;G@m?#|F<&~a$f5(N=RB9a4cg6BEj$h$thR5@BuiPqm|LR}<;b=(u zkB=Wv^5GrXW4+^a5g*@XkZ;K2vcimnb7#Xcu23w`TeLn1tvR-xzNPl##*M4d2CGi; zv!dfk{}Ip6yMBZJCg~o(@cCo1!OA4LZ@yRlfZu+<_QCf8J~^Hu{;{9<#}VQmw7-sc zi1#J%_uQlaSLEmsM-JxqUQrM%YQJ;H*s73pA@rRU^GlJB51>_|k%Vl-)81J;!hU`N z{Q1}6#*ZWZ5k>st6!DLvSn(+wJ|8Zj?|^=Z)K)0hRsQSqk1=}7xvz4N-_{+(-Zx2g zmMv6U;GB#Z2=@wPK=9JaiXcpnizY)yKrwMDJe=*ITHWaCMhH10Ryv_&@g+9nPv|g z94uX3<7dSC+&=Sv~V7(04Ywe>$FXiIfi$N^wpN8SUrP1m1DiV)@&l=ChgCphV2UYAzSULj zu()w>9<5hJ;~oaaKPbjeHU7~r;385A`0z?A{VDJ!If?Je(9`!kUyk@k=-%BmHKm;1 ziSZAzh2h#9fv=~)=HXlB9a-O>NAdL8Z_lnB9RJv5HcdR#ocKrBR*KO_Yc7Ylf$@)z zWJ^&}LhT*#k1~1=wk@Hyub6yH89#kT<6$6n94=(4QThDF5&0}P{auP{9opIJq<0_>sN7=8$I|Tp7yIIOaE}eaqUCZua_kN2^ z!9DERSsqwkSe{tkSRUU}UK#&jc^AlL+rEAKiYY5AH`UbC+;RH!X^!WW^Suvb>-`*u z4j$x_??S{TPmhtF9vj8UA8TQB#97W3>q*LevfnkiDkdhb(5K%#k^Xwl216{r7rkdi|DV=HHS^^wf)nf{})9hT6T zfa(hK4Pj#=;xP!_w-3QIkHO{@7`Bst7ui1*(U?_{)*s{bUK-3()mvAx?#gS}MrOn8 zcm_kSnJ*G1SGiqIOEHxoC}9P2?h4M1wB zC+_(=&z-<~;VX0JW_%s`3h{x&SdAg^%hg@=KcR z9}IugS0PabfN%pqaRY7)_(Opc>o!-Bd-d+k3z`1Mz4-BHi-vmAf&~lSm6eqpzI5r* zR(P+g`l%X@tTjkBQi~*-b>+YvPs)2k|J*OXiLSCZMYY?!fIsYjKb(l$>H+%)aER3@ z;#?z%3rqyz?nT%?ZW7(T2QpsRLclA>okEA3n5^b0-DPnYPvWkY&{w`rxAv)9_v&-5veKsW<4C!{MD`kWY=g=tyyFm-G)F zY9Os@+ocB!|ImQ$RbJEJD3OC z6u$DxKT8fN*>oT{ku)q_$=drAa-?KZ*y&JkPrH)0r#s@G zeCW)nAWBb70sp-`c&H62+sTM>eJuF(KjeRzK4$n(eheBj)^5;{Ay%ZYIiJ?;en8Sj zy)WwwACTE^^FulOw)5WjHp2r_YVOIwJ09Q^KxUCa>k3pCZR`>j{Vvv`R@naPuS1uEV zKfv+B;}7_qH~HRu?hr>efIsZA>t2f3L(esaKWw9U{Bay(h43eC|6qAgx`I8#?H_E8 zD9&>w?(-mE4;Bx?{=D2>c>Lk2v}nL_W#ErM;14e|U<6Z2Fa-Vp#;B>m^V$M~)IP-^ z%+68>kKp6(rSuGcMCeLlF75^! zK@jC*evbXBXai}7m|g1{9!9vp7yb<9?14>jxZ*Ln9d4gHPETNOV1Dygj_#zh+m@## z?pN*9r}3Pr^0=^VE7I6G>|9ny3F_)lmsp&za2#thYdgCJb}hIj`v~1tRrzNf_-kd? z%&r}OJQn);>#zG`?f4D&TScU#r0}*TwjRy$QF?lM)`C011K9j?Cr3wq+y?j6(49MX z+{%HUJBtTNON=MjLyn9cJAUZ2Y5(17o+apMs}%4DVmjb^)hN(y3!G_JSXk(ArU(wFp znEUhZzE8F}^_w7L!s8Du;14C>4@KaQ4Zt7rz#r>@KcpQ~8}Uak=x2G{=gph98@89G z`M+q_jzN1=6g+?^G)a`Dav(0{+Z*=^IsMm1MiNXITfC?qDP@3 z@VwbmrsHlpskS;weEbo&f3UR=!QRobWcCm7SaZSt!FcvMvVX9(kCx+)$sWTixwwa& zz{3$4V3AgO!8n@BvP+=Kdk@d-(`X3PTTh)ygryD^X{yTL7j=`XxPQ6qVb*2WqxQ&K zbcgos-F*)+jPEKbDvrQ2)9+1Mapo{q{ei|swuzhKUfp1FojRJ+xI zIJpDFDIX+G>j-g%j>K7d66bV+IKMMQVX;KXu!S&we8eeH1+X$=%PXk>-d6W1lWtNv zU7%=qV4dW`$ql%}8o0xlb3^=x1L8ldfj)>r#+^Rjoitk*soLl!-hM?ELd@cq_ywSs%>{$$&!tiNfLICd$}EB4?GL} zAHyJW+Y)GzN?NPBSp93EXA%7I2M#=NdBA{=hV=FOuRHqLI^b?QciMFV@ze0XvU2mK zYu9gp2Q7nQqNB+7_%RCbIZmNLffNgSVmdhS?`LOG!Zu~fbA|tbC)O7O4vM{h8~Jxf zk98ab97r3s7SI-}Lee_Y`?C2^A<1dxA^yO+dcc4;oBV(6kW-AseAHk*Q4bqMJ)8=o z%)g=X>lnwPmFZ!mVvnUTMum#6=u+(AdB8OY7vpUpcw!LWE;@X;34e?kBXV0Sb(f@d z?}In;-U}6`W7V2F;9rwF_R1@xC2-Rs{=aYavo;(R+4K)U978euRhXTcm24leb8y3& z(kA}sjr+`?dRyTSd;0;7j*h?|h+$xU*uWso{jeIju$pWjm?O8% zhgD3>@23>YRc!uS0y_xfP{7=yP5f~a_`~I1?Mo52`?YlaDgI#oSHAd-*&nkhuvm@@ z7cNkGdO9Ef!E7GFyB|KxJhl+_SsyS=(efM?lP(;?;?Ehj6!zJ>UwHo?2hPQBJk?Ek z(Ex8%;14!8ISzc_wV%fy)x7PAVGzU|_Yi|L;Sa=r6k_b->xYlXaTPKAp(jb@Nq)rU z+4RSEkx%xBL-ypjBZu2J2lyJcvK?Kfm|hdkY~=*t)ap6uw7Z$pOH=wusr_1@Tvn{#U_)b}@e5 z4ngq0V!nNKZ2n)#VmQiRhd}5rmY;b(EaVtqHV?#(Kv=d_ze6ro8-$Yr{I%C*B82(TO{!n^~KNNiKkgVr*TIZNX=$q%(&wNXl zELq|R{^FtEefM2cf1<_wJt4W^*MVE5R@Eh)38utgKe~A_9lZW=bQW>Zt~LkBY{ypG zsVYwfY6>Lt$8YrgobO0!*Nf1sS->B6 zfIn{F{{kh$`f--dkPrN<961lN1n$_)S%Lq`oK!d?;146^w$k&%R5-`Yzd!1=}k#&dqM~rkk{O!C%lF@z8YTmb8`zo#89834x z!5;1TVm&UYCVAa+v}DuyR&~ngJ)rOZNWnaKlh@!O^9S_x`>(6|nICj%c_n?dMwN{1 zT)|6xi7sBeNXbb_6de^sk>O|IYZXoBQ=MpU!ptk%Y6(U)f8P-yno^Q%--)U z-oJS0|7*l3=Rt!8JCMPl$8^-K;>Cz-1#LGg#Jt*e16gtr&4U_R~A-$|vI zGqZg*Uk3(hQj2*V#(du@e3{sI51%!&rO1q|}CH9wkFx0&lOuwY>7YE8gqMg5BC1YdN!|*%{v(Wa93G0z*p@l{_r;E$0%n1c!F_& z`L&gTGb;&WTvBo}v7D62$tk?B{X~3z?%X*_NPz!#TpY#4#PI7du~>^~^uxNzu%qCWlB@;~5RX03Kz=@$Y_9qm;&G41gHJB! z6!OAci=et2QQ#^)-N;dz>QCjo1n~HP*&EpRRrvlk;&xGIRm>ilttplpyA(qu=w)T{ z0o7My_pH-qb+EcvoxE;lj{z1bPYrmg+n;X>YZLpPwUM=zwK-CMm3NAzdK1qUs~2&` zzp-m=yylgptgPJc&p-cs8#%de#Lx;Dcf zz#uhnKWXyAVxhnduy14T+Vscp2ku<~e*i-v_t%Cf_;rqf{h_IipXj>wn@qX+uYmn0)26Aj*%F+cwJW0QXIzR*)x|BAlcy1K0NB+5>S zqbKmkdWbdj%g8Sl3N8yrb0ad}zKQm4-AHna7SIpCAIi&@P!h{CmM0SKYm0w~LB^Z}UBo%d-tB zIF1|u$aRCDP%`_6um;5Jpo0B_t$lQ4|7dIcF*DhF zCI4O)xjH&$mZ57yWws_o>2?Khnzohs){bF~v z345fZ=<2OTK6!TkivKYTvPu)YzJ_<2>XbqKat68d-sfi#1N^7@u;CkC>Ff7j z*YqPdAJ(I?va;y%m8*2=(q;ZV5gQvzVPRq52MVPyB~`9pbWF(p`iN!6JKh?P`D45)IC zr94rMDz52KqUa|+PW5r&Lq68z_RSk)YjwB+>-xi38zxS1E0WhKAX)R=mm&(#HDQud z;j8_}j`&Z|&)N{^d;HZvorb@_(qQsS)pQkNI1B*n{B}=I_A# z;#s~Bb`Hx?-5K_H2wNxnWAhG%KRi?y5Aai83|>Fv$nZTtMZgI0;F)E(uMA@WZ?newVQ2AGdxk$EbeB~`>94AeH(LK2n$@Ol0=VSI$ek-AVs}e( zH#`up7VQq-rZ8gsq=Hl-e?kF#oid#D3H8;)7)rS(Td2L+UY%Y2Jo$jin=yuK-C3Qi zZl;6j67;f?wS~2bwT-nga;IculDYDa?deD`mivueGrM+eZSA+f3!Tl@eh{B@4!C0- z=IU+6%Ch>DloVgVI_~<&$jGLc^f2(DV1A_np58X+Snah#_}s?EdI0!!8?i@i6|hH* zZ7l?GS0dQh9PZq?;|h!dyn$Hrs!ASr@Yn|Okz2TcarS;V9D|;?&gb2Z z(pLb7!#a(SEvuj0bT#JV&kKT)OD6z!?Kb3L&_TN4s81oASBEp*;^WVYwtKL(kCx*P zat5BbfBPn#^!Fui$K4qz{;Elp7j$B{N71*a@@^qLx&MfsF+ylTHmxD152M0hSrm_inx#_)%i+TsBL8c*?uAMl5-ImH<& z@b)4W!(R&SqMl+97L#5A?3bdaN{@VjKm1WY@Q2^VXZQm=qVIs$XBNAkI}`B!%uX1J)<+oCTSj+kJb6bqHvU8y_TVAZUcU8~;#7nCM&#~xtEkKrHG?z1k`3IFw> z$OUq%oZ&+F$mPSA6?u8CV2yXgykCCl^n9Tn@e;8{jrgOs+7{eaw(za8y%c%Y_I%iB zQF3UoeWbsSv$b`j{}pfJ09Ix`R!t)KU7=F(KjwGEFtMJVUe4#AfBr78M=L+lPj`6q zo4CQU34f@-|4P6gihjsr;(d$c+^_KXgZY2;M8BqT!*#?E94?X>_;8r zvMS6t--ti*fhW!be*`qgAF9h2(@#HwCw=8o9)ILrO5yQGc|k6Jd?WraCJWsyJYG>= zx`>eP<1P4KwTlr?Yps?l8yaOWsT{R;S#cBlI}J z8F?^8RCva^Z8>{{|31-F9kE9;@~qC5;}84I*x1w6@rR99Y#l_1JL~TFV{2updjWpx zsY&O^;qdMUkzN`pS!WgE>oayn)15=-=&mS{a(vSp%I-dHhChG-xWf1}?qM{zsp8LZ z1u@YL1#yv8xk*uB!QNTv$%%9+6}e;nSa4wcxN*a0OrO@~v(Y8}*^Y^H&n*#rz=>Rr zUm}-#GLg#*P2_l9tmo6ueuL%z{>dywR*@PMREXQO1sFsTIS7G447U+?2zC&6bK*jP zL6Q)go(YbuLVx0_PEtK$Jjyw6b^v!==e&VGT!BCAfIlqhFnq1{a=N4q?4iObBL})B z@_(q1+<9d}USa$3(WXI;A@NfO_-jF6zH|ErSAl zeaU3+UjMy@hWwg@#3;uI*Q@AD9*8--M}BfG0b{y0Bz^@Ct2k=LA1*FopvaG}wwr~+J zyj^~7jsyIc?z0?(f-tTGabY+^5Ef(3gvELzZvLK@mexPO5#8qc^QSw#`b|9H&-=-?8#TDZ|zI*KorN>56I50?ni#_Zh`-$ZM0be1hNiL(a9?n#9KMUhQIX%HS zIk(}BA0JI&u$crn+rtiG!j3N}6q>+#3k(czPm;1?;tSYdO}tSZoXTY9p$Owz+R#B#j53q*kL+{O=J@4&Vv)cXLyQKfdo=D0x zjig(ak%&!+VZGKh#2H=j(uzzR+duS^Ww`UV^*4q*P z0Ss~mc92xWh};H8)+6AMYB#EDa0mWCPF3()UFX0Xh#2!w?f`kh=gOMXBO`7L>>nzS zO{57?tlvQMHRjiin>6mDcfWnF={|g3{Wq)J=zIGBQcQ;(B)^VK{DLVhEe(D^(R2vo zf}?{2nIY%sH*+-TN2xXSE%i^B8u-uA69PzPe-5cx7c|WQrA_b9GVNy=!4A-s=R{YHj{Pr&&b zBC@f0L%8N~Q#`bPmQE*St7rF~`2E%1wvOl#AFJQq_icTy0v-Qeod*1|ddv9NwD@PR zuk{Q4z{*t%{}>Ek3R?dA{K0E}|E=G$@Bh{B-ygo)@2i<_G|8_Yywy+j=l|}vTk_{N z*L1||>umeDmI}Dx*SvILdWQ~ejz1h79S69&x;ENB;LA|*D4*iJ-Qk1f(WxKSIoOO? zEIPv<37DVx`}>n0aIUYf55$+>h7WsH|Ni|)BBx2y*z50nsIH*~+!_@HL>J);b>0&^ z>s~}xjuB-XhrJu0-Gbfw4(t?n{fV-H7b@})|M!Tlrp6M^?}^m;I36@-zs-_k`t+f@ z3VraBW%03{41Y{H4*U_gxo!NxVhf523wS$HPwXI!o0sJWNfx(%0Dqj=)QCSu4;$3( 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 K_gCNc*Z%-FSz|o_ literal 0 HcmV?d00001