From db4c1b0f1e876dd9330d468f58bd040f0e134b30 Mon Sep 17 00:00:00 2001 From: Simone Date: Wed, 29 Apr 2026 23:19:34 +0200 Subject: [PATCH] initial: nsSplashPNG plugin 1.0.0 --- .editorconfig | 24 + .gitattributes | 51 ++ .github/ISSUE_TEMPLATE/bug_report.yml | 76 +++ .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 32 + .github/PULL_REQUEST_TEMPLATE.md | 31 + .github/workflows/build.yml | 66 ++ .github/workflows/release.yml | 99 +++ .gitignore | 71 +++ CHANGELOG.md | 19 + CONTRIBUTING.md | 137 +++++ LICENSE | 21 + README.md | 348 +++++++++++ README_IT.md | 211 +++++++ SECURITY.md | 40 ++ VERSION | 1 + build_plugin.py | 679 +++++++++++++++++++++ dist/amd64-unicode/nsSplashPNG.dll | Bin 0 -> 113152 bytes dist/x86-ansi/nsSplashPNG.dll | Bin 0 -> 92160 bytes dist/x86-unicode/nsSplashPNG.dll | Bin 0 -> 94208 bytes docs/QUICK_REFERENCE.md | 157 +++++ docs/README.md | 306 ++++++++++ examples/example_advanced.nsi | 47 ++ examples/example_basic.nsi | 39 ++ examples/example_fade_speed.nsi | 50 ++ examples/example_formats.nsi | 43 ++ examples/example_nonblocking.nsi | 34 ++ examples/images/splash.bmp | Bin 0 -> 262282 bytes examples/images/splash.gif | Bin 0 -> 7576 bytes examples/images/splash.jpg | Bin 0 -> 13543 bytes examples/images/splash.png | Bin 0 -> 12528 bytes src/SplashPNG.cpp | 553 +++++++++++++++++ src/api.h | 83 +++ src/nsSplashPNG.def | 4 + src/nsSplashPNG.vcxproj | 187 ++++++ src/nsis_tchar.h | 243 ++++++++ src/pluginapi.c | 298 +++++++++ src/pluginapi.h | 104 ++++ tools/fix_repo.py | 137 +++++ tools/push_and_disable_gitea_actions.py | 80 +++ tools/squash_to_single_commit.py | 121 ++++ 41 files changed, 4400 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README_IT.md create mode 100644 SECURITY.md create mode 100644 VERSION create mode 100644 build_plugin.py create mode 100644 dist/amd64-unicode/nsSplashPNG.dll create mode 100644 dist/x86-ansi/nsSplashPNG.dll create mode 100644 dist/x86-unicode/nsSplashPNG.dll create mode 100644 docs/QUICK_REFERENCE.md create mode 100644 docs/README.md create mode 100644 examples/example_advanced.nsi create mode 100644 examples/example_basic.nsi create mode 100644 examples/example_fade_speed.nsi create mode 100644 examples/example_formats.nsi create mode 100644 examples/example_nonblocking.nsi create mode 100644 examples/images/splash.bmp create mode 100644 examples/images/splash.gif create mode 100644 examples/images/splash.jpg create mode 100644 examples/images/splash.png create mode 100644 src/SplashPNG.cpp create mode 100644 src/api.h create mode 100644 src/nsSplashPNG.def create mode 100644 src/nsSplashPNG.vcxproj create mode 100644 src/nsis_tchar.h create mode 100644 src/pluginapi.c create mode 100644 src/pluginapi.h create mode 100644 tools/fix_repo.py create mode 100644 tools/push_and_disable_gitea_actions.py create mode 100644 tools/squash_to_single_commit.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9182a7d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{yml,yaml,json}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{cmd,bat,ps1}] +end_of_line = crlf + +[*.{nsi,nsh,sln,vcxproj,filters}] +end_of_line = crlf + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..de4a923 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,51 @@ +# Normalizzazione line endings +* text=auto eol=lf + +# File di codice +*.c text eol=lf +*.cpp text eol=lf +*.h text eol=lf +*.hpp text eol=lf +*.def text eol=lf +*.py text eol=lf +*.ps1 text eol=lf +*.cmd text eol=crlf +*.bat text eol=crlf + +# NSIS +*.nsi text eol=crlf +*.nsh text eol=crlf + +# Markdown / docs / config +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.json text eol=lf +*.txt text eol=lf +*.xml text eol=lf + +# Visual Studio (CRLF richiesto) +*.sln text eol=crlf +*.vcxproj text eol=crlf merge=union +*.filters text eol=crlf merge=union +*.rc text eol=crlf working-tree-encoding=UTF-16LE + +# Binari +*.dll binary +*.exe binary +*.lib binary +*.obj binary +*.pdb binary +*.zip binary +*.7z binary +*.png binary +*.ico binary +*.gif binary +*.jpg binary +*.jpeg binary +*.bmp binary +*.pdf binary + +# Linguistic detection (GitHub Linguist / Gitea analogo) +*.nsi linguist-language=NSIS +*.nsh linguist-language=NSIS diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..b11a09f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,76 @@ +name: Bug Report +description: Segnala un bug riproducibile +title: "[Bug]: " +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Grazie per la segnalazione. Compila i campi sotto per aiutarci a riprodurre il problema. + + - type: input + id: plugin-version + attributes: + label: Versione plugin + description: Contenuto del file VERSION o tag della release scaricata + placeholder: "1.6.0" + validations: + required: true + + - type: dropdown + id: architecture + attributes: + label: Architettura DLL usata + options: + - x86-ansi + - x86-unicode + - amd64-unicode + - non so / multiple + validations: + required: true + + - type: input + id: nsis-version + attributes: + label: Versione NSIS + placeholder: "3.10" + validations: + required: true + + - type: input + id: os + attributes: + label: Sistema operativo + placeholder: "Windows 11 23H2 x64" + validations: + required: true + + - type: textarea + id: repro + attributes: + label: Passi per riprodurre + description: Script .nsi minimale che riproduce il problema + render: nsis + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Comportamento atteso + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Comportamento osservato + description: Includi messaggi di errore esatti, output del log NSIS, screenshot + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Note aggiuntive + description: Qualunque altra informazione utile (workaround tentati, ecc.) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..2126b20 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Domande generali + url: https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/issues?type=question + about: Per domande di utilizzo che non sono bug, apri una issue con label "question" + - name: Security + url: https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/security/advisories/new + about: Segnala vulnerabilità di security in privato (non aprire issue pubbliche) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..bc28f74 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,32 @@ +name: Feature Request +description: Suggerisci una nuova funzionalità o miglioramento +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problema / Caso d'uso + description: Quale problema vorresti risolvere? Quale workflow vuoi abilitare? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposta + description: Come dovrebbe funzionare? Includi esempio di API/uso negli script NSIS se possibile. + render: nsis + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternative considerate + description: Altri approcci che hai valutato e perché sono stati scartati + + - type: textarea + id: additional + attributes: + label: Note aggiuntive diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3db4c88 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ +## Descrizione + + + +## Tipo di Modifica + +- [ ] Bug fix (modifica non breaking che risolve un problema) +- [ ] Feature (modifica non breaking che aggiunge funzionalità) +- [ ] Breaking change (fix o feature che rompe la compatibilità) +- [ ] Documentazione +- [ ] Build / CI + +## Architetture Testate + +- [ ] `x86-ansi` +- [ ] `x86-unicode` +- [ ] `amd64-unicode` + +## Checklist + +- [ ] Build completa senza warning: `python build_plugin.py` +- [ ] CHANGELOG.md aggiornato (sezione `[Unreleased]`) +- [ ] README / docs aggiornati se l'API è cambiata +- [ ] Esempi aggiornati se l'API è cambiata +- [ ] Conventional Commits rispettato +- [ ] Nessun file in `dist/` o `__pycache__/` committato +- [ ] Self-review completata + +## Note Aggiuntive + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..4102e1e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,66 @@ +# Build (GitHub) — pipeline CI primaria +# +# Strategia hosting: +# - Repo primario su Gitea (push iniziale) +# - Push mirror automatico Gitea -> GitHub +# - GitHub esegue TUTTA la CI (Gitea non esegue Actions) +# +# Trigger: +# - push su main (incluso quello propagato dal mirror Gitea) +# - PR aperte su GitHub +# - run manuale via UI + +name: Build + +on: + push: + branches: [main] + paths-ignore: + - '**.md' + - 'docs/**' + - 'examples/**' + pull_request: + branches: [main] + workflow_dispatch: + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + build: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + config: [x86-ansi, x86-unicode, x64-unicode] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + + - name: Read VERSION + id: version + shell: pwsh + run: | + $v = (Get-Content VERSION -Raw).Trim() + "version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Build ${{ matrix.config }} + run: python build_plugin.py --config ${{ matrix.config }} --verbosity minimal + + - name: Upload DLL + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ steps.version.outputs.version }}-${{ matrix.config }} + path: dist/** + if-no-files-found: error + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e4cf67d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,99 @@ +# Release (GitHub) — pipeline release primaria +# +# Triggerata quando un tag v* viene propagato dal mirror Gitea. +# Costruisce gli artifact per tutte le architetture e crea la GitHub Release. + +name: Release + +on: + push: + tags: ['v*'] + workflow_dispatch: + inputs: + tag: + description: 'Tag versione (es. v1.7.0)' + required: true + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + strategy: + fail-fast: true + matrix: + config: [x86-ansi, x86-unicode, x64-unicode] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - uses: microsoft/setup-msbuild@v2 + + - name: Build ${{ matrix.config }} + run: python build_plugin.py --config ${{ matrix.config }} + + - name: Determine version & arch + id: meta + shell: pwsh + run: | + $tag = "${{ github.ref_name }}" + if ([string]::IsNullOrEmpty($tag) -or $tag -eq "main") { $tag = "${{ inputs.tag }}" } + $v = $tag -replace '^v','' + $map = @{ 'x86-ansi' = 'x86-ansi'; 'x86-unicode' = 'x86-unicode'; 'x64-unicode' = 'amd64-unicode' } + $dir = $map['${{ matrix.config }}'] + echo "version=$v" >> $env:GITHUB_OUTPUT + echo "tag=$tag" >> $env:GITHUB_OUTPUT + echo "dir=$dir" >> $env:GITHUB_OUTPUT + + - name: Package + shell: pwsh + run: | + $name = "${{ github.event.repository.name }}-${{ steps.meta.outputs.version }}-${{ steps.meta.outputs.dir }}" + $stage = "stage/$name" + New-Item -ItemType Directory -Force -Path $stage | Out-Null + Copy-Item "dist/${{ steps.meta.outputs.dir }}/*" $stage -Recurse + Copy-Item README.md, LICENSE, VERSION, CHANGELOG.md $stage + if (Test-Path "include") { Copy-Item "include" $stage -Recurse } + if (Test-Path "examples") { Copy-Item "examples" $stage -Recurse } + Compress-Archive -Path "$stage/*" -DestinationPath "$name.zip" -Force + + - uses: actions/upload-artifact@v4 + with: + name: pkg-${{ matrix.config }} + path: '*.zip' + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: SHA256SUMS + run: | + cd artifacts + sha256sum *.zip > SHA256SUMS.txt + cat SHA256SUMS.txt + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: | + artifacts/*.zip + artifacts/SHA256SUMS.txt + generate_release_notes: true + body: | + **Repository primario**: vedi link Gitea nel README. + + Il mirror GitHub è generato automaticamente dal repo Gitea (sorgente di verità). + Per issue e PR aprire ticket sul repository primario Gitea. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76d88c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,71 @@ +# Build output (src/ subdirs only — dist/ is tracked, contains pre-built DLLs) +bin/ +obj/ +Build/ +build/ + +# Visual Studio +*.user +*.suo +*.ncb +*.sdf +.vs/ +ipch/ +*.VC.db +*.VC.VC.opendb +*.opendb +*.aps +*.tlog +*.idb +*.pdb +*.ilk +*.exp +*.lib +*.obj + +# Python +__pycache__/ +*.pyc +*.pyo +.pytest_cache/ +*.egg-info/ +.venv/ +venv/ + +# Editor / IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +Thumbs.db +desktop.ini +.DS_Store + +# NSIS +*.nsh.bak + +# Logs +*.log +.logs/ + +# Sensitive +.env +.env.local +*.pem +*.key + +# Migration/legacy scripts (temporary, not part of the plugin) +tools/t*.py +tools/check_*.py +tools/legacy/ + +# MSBuild output directories in src/ (build artifacts, not source) +src/**/Release/ +src/**/Release Unicode/ +src/**/x64/ +*.recipe +*.iobj +*.ipdb diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0249157 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +Tutte le modifiche rilevanti a questo progetto sono documentate in questo file. + +Il formato segue [Keep a Changelog](https://keepachangelog.com/it/1.0.0/), +e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] — 2026-04-29 + +### Added +- Prima release come repository indipendente +- Build script unificato (`build_plugin.py`) con CLI canonica +- CI/CD via GitHub Actions (mirror automatico Gitea → GitHub) +- Documentazione completa (README, CONTRIBUTING, SECURITY) + +[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/compare/v1.0.0...HEAD +[1.0.0]: https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/releases/tag/v1.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a1f95df --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,137 @@ +# Contribuire a nsSplashPNG + +Grazie per il tuo interesse a contribuire! + +> Repository primario: [https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng](https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng) +> Mirror GitHub: [https://github.com/systempal/nsis-plugin-nssplashpng](https://github.com/systempal/nsis-plugin-nssplashpng) (read-only — apri PR su Gitea) + +## Prerequisiti + +| Strumento | Versione | Note | +|-----------|----------|------| +| Visual Studio | 2022 / 2026 | Workload: Desktop development with C++ (toolset v143/v145) | +| Python | 3.7+ | Per `build_plugin.py` | +| Git | 2.30+ | Con supporto submoduli se applicabile | + +## Quick Start + +```powershell +git clone https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng.git +cd nsis-plugin-nssplashpng +python build_plugin.py +``` + +Output in `dist/{x86-ansi,x86-unicode,amd64-unicode}/`. + +## Workflow Sviluppo + +### 1. Branch + +```powershell +git checkout -b feat/nome-feature # nuova feature +git checkout -b fix/descrizione-bug # bugfix +git checkout -b docs/aggiornamento # solo doc +git checkout -b chore/manutenzione # build, dipendenze +``` + +### 2. Commit + +Segui [Conventional Commits](https://www.conventionalcommits.org/it/v1.0.0/): + +``` +feat: aggiunge supporto per formato XYZ +fix: corregge crash su Windows 11 ARM +docs: aggiorna esempio di utilizzo +chore: aggiorna toolset MSVC +refactor: estrae logica parsing in funzione separata +test: aggiunge test per edge case +build: aggiorna script di build +ci: aggiorna workflow GitHub Actions +``` + +Breaking changes: + +``` +feat!: rinomina API Foo() in Bar() + +BREAKING CHANGE: Foo() rinominata in Bar(); aggiornare tutti i chiamanti. +``` + +### 3. Pull Request + +PR aperte verso il branch `main` su Gitea. Verifica: + +- [ ] Build completa senza warning per tutte le architetture: `python build_plugin.py` +- [ ] Esempi aggiornati se cambia l'API +- [ ] `CHANGELOG.md` aggiornato (sezione `[Unreleased]`) +- [ ] Documentazione aggiornata (README, docs/API.md se esiste) +- [ ] Conventional Commits rispettato +- [ ] Nessun file in `dist/` o `__pycache__/` committato + +## Build Script + +``` +python build_plugin.py [opzioni] +``` + +| Opzione | Default | Descrizione | +|---------|---------|-------------| +| `--config` | `all` | Architettura: `x86-ansi`, `x86-unicode`, `x64-unicode`, `all` | +| `--toolset` | `auto` | Visual Studio: `2022`, `2026`, `auto` (rileva il più recente) | +| `--jobs` | numero CPU | Thread paralleli per la build | +| `--clean` | false | Pulisce `dist/` prima di compilare | +| `--install-dir` | none | Copia DLL anche in `/{arch}/` (es. NSIS plugin folder) | +| `--verbose` | false | Output dettagliato (mostra comandi MSBuild) | + +Exit codes: +- `0` — Successo +- `1` — Errore di build +- `2` — Prerequisiti mancanti +- `3` — Argomenti invalidi + +## Release Workflow + +Le release sono automatiche via tag Git: + +```powershell +# 1. Aggiorna VERSION e CHANGELOG.md +echo "1.7.0" > VERSION +# (modifica CHANGELOG.md spostando entry da [Unreleased] a [1.7.0]) + +# 2. Commit +git add VERSION CHANGELOG.md +git commit -m "chore(release): v1.7.0" + +# 3. Tag annotato +git tag -a v1.7.0 -m "Release v1.7.0" + +# 4. Push +git push origin main +git push origin v1.7.0 +``` + +Il workflow `.github/workflows/release.yml` compilerà tutte le architetture +e creerà la release su Gitea con gli artifact ZIP allegati. Il mirror GitHub +riceverà automaticamente il tag. + +## Code Style + +- C/C++: stile coerente con il file modificato +- Python: PEP 8 (4 spazi indent, line length flessibile) +- Header file: `#include` ordinati: stdlib → Win32 → progetto +- Encoding: UTF-8 senza BOM (eccetto `.rc` che richiede UTF-16 LE) +- Line endings: LF in repo (`.gitattributes` normalizza) + +## Reporting Bug + +Apri una [issue](https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/issues/new) usando il template "Bug Report". +Includi: +- Versione plugin e architettura DLL usata +- Versione NSIS +- Sistema operativo +- Script `.nsi` minimale che riproduce il problema +- Output / errore esatto + +## Domande + +Per domande generali, apri una [issue](https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/issues) con label `question`. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2056c23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Simone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..da181fa --- /dev/null +++ b/README.md @@ -0,0 +1,348 @@ +# nsSplashPNG - PNG Splash Screen Plugin for NSIS + +A modern NSIS plugin that displays splash screens with full alpha transparency support using WIC (Windows Imaging Component). + +## Features + +- **Multiple Image Formats**: PNG, JPEG, BMP, GIF, TIFF, ICO via WIC decoder +- **Full Alpha Transparency**: Proper PNG/GIF alpha channel support via WIC and layered windows +- **Configurable Fade Speed**: Customizable fade in/out speed (1-255 steps, 30ms-7650ms duration) +- **Non-Blocking Execution**: Script continues after fade-in while splash stays visible +- **Auto-Close Timer**: Configurable automatic close after specified milliseconds +- **Click-to-Close**: Optional user interaction to close splash +- **No Cancel Option**: Prevent users from closing splash during critical operations +- **Manual Control**: Programmatic show/stop with fade effects +- **Multi-Monitor Support**: Center splash on primary, current (mouse), specific index, or nearest to a screen coordinate +- **Zero Warnings**: Clean compilation with Visual Studio 2022 + +## Installation + +1. Copy `nssplashpng.dll` to your NSIS plugins directory: + - `x86-unicode`: For 32-bit NSIS Unicode builds + - `x64-unicode`: For 64-bit NSIS Unicode builds + - `x86-ansi`: For 32-bit NSIS ANSI builds + +2. Include the plugin directory in your script: + ```nsis + !addplugindir "path\to\plugins\x86-unicode" + ``` + +## Building from Source + +### Prerequisites +- Visual Studio 2022 or later with C++ build tools +- Python 3.x + +### Build Steps +```powershell +cd nsSplashPNG +python build_plugin.py # Build all architectures +python build_plugin.py --config x86-unicode # Single architecture (x86-ansi|x86-unicode|x64-unicode|all) +python build_plugin.py --vs-version 2026 # Specific VS version (2022|2026|auto) +python build_plugin.py --clean # Clean dist/ before build +python build_plugin.py --install-dir "C:\NSIS\Plugins" # Copy to additional NSIS directory +python build_plugin.py --verbosity minimal # MSBuild verbosity (quiet|minimal|normal|detailed|diagnostic) +``` + +The script will: +- Build for x86-unicode, x64-unicode, and x86-ansi configurations +- Copy DLLs to `dist/{x86-unicode|amd64-unicode|x86-ansi}/` directories +- Clean up intermediate build artifacts from `src/Build/` +- Supports parallel builds with automatic CPU optimization + +## Usage + +### Important: Plugin Memory Management + +**CRITICAL**: Always use `/NOUNLOAD` flag on the first `show` call to keep the plugin loaded in memory. Without this, NSIS unloads the plugin after each call, losing all state (window handles, threads, etc.), causing `stop` to fail. + +```nsis +; Correct - keeps plugin loaded +nssplashpng::show /NOUNLOAD 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +nssplashpng::stop /FADEOUT + +; Wrong - plugin unloaded after show, stop will fail +nssplashpng::show 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +nssplashpng::stop /FADEOUT +``` + +### Syntax + +```nsis +nsSplashPNG::show [/NOUNLOAD] [/FADEIN [step]] [/FADEOUT [step]] [/NOCANCEL] [/MONITOR ] +nsSplashPNG::stop [/FADEOUT [step]] +``` + +**Note**: The timeout must be specified immediately after `/NOUNLOAD` (if present), followed by optional flags, with the image path as the last parameter. + +### Parameters + +#### show function +- `/NOUNLOAD` - **Required for stop() to work**: Keeps plugin in memory between calls +- `/FADEIN [step]` - Enable fade in effect. Optional step (1-255) controls speed (default: 15) + - Higher step = faster fade (255=instant, 51=fast, 15=normal, 5=slow) + - Duration formula: `(255 / step) × 30ms` +- `/FADEOUT [step]` - Enable fade out effect when auto-closing via timer (optional step) +- `/NOCANCEL` - Disable click-to-close (splash can only be closed programmatically or by timer) +- `/MONITOR ` - Select the monitor on which to center the splash (default: primary monitor) + - `PRIMARY` - Primary monitor (default) + - `CURRENT` or `MOUSE` - Monitor where the mouse cursor is + - `1`, `2`, `3`, ... - Monitor by 1-based index (enumeration order) + - `POINT x y` - Monitor nearest to the given screen coordinates (uses `MONITOR_DEFAULTTONEAREST`); useful to target the monitor where an application window was last open +- `` - Auto-close timer in milliseconds (0 = manual close only) +- `` - Path to image file (PNG, JPEG, BMP, GIF, TIFF, ICO) + +#### stop function +- `/FADEOUT [step]` - Enable fade out effect when closing. Optional step (1-255) controls speed (default: 15) + +### Examples + +#### Basic Usage +```nsis +; Show splash for 3 seconds (PNG with alpha) - no /NOUNLOAD needed for auto-close only +nsSplashPNG::show 3000 "$EXEDIR\splash.png" + +; Show JPEG splash (opaque, no transparency) +nsSplashPNG::show 3000 "$EXEDIR\splash.jpg" +``` + +#### Fade Effects +```nsis +; Default fade speed (step 15, ~510ms) +nsSplashPNG::show 3000 /FADEIN /FADEOUT "$EXEDIR\splash.png" + +; Fast fade (step 51, ~150ms) +nsSplashPNG::show 3000 /FADEIN 51 /FADEOUT 51 "$EXEDIR\splash.png" + +; Slow fade (step 5, ~1530ms) +nsSplashPNG::show 5000 /FADEIN 5 /FADEOUT 5 "$EXEDIR\splash.png" + +; Instant (step 255, ~30ms) +nsSplashPNG::show 3000 /FADEIN 255 /FADEOUT 255 "$EXEDIR\splash.png" + +; Different speeds for fade-in and fade-out +nsSplashPNG::show 3000 /FADEIN 51 /FADEOUT 5 "$EXEDIR\splash.png" +``` + +#### Manual Control +```nsis +; CRITICAL: Use /NOUNLOAD when using stop() to close manually +nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL "$EXEDIR\splash.png" + +; ... do installation work ... + +; Close with fade out +nsSplashPNG::stop /FADEOUT + +; Or close with custom fade speed +nsSplashPNG::stop /FADEOUT 51 +``` + +#### Real Installer Example +```nsis +Name "My Application" +OutFile "setup.exe" +InstallDir "$PROGRAMFILES\MyApp" + +!addplugindir "plugins\x86-unicode" + +Page instfiles + +Section "Main Installation" + ; Show splash with fade in, prevent user closing, no auto-close + ; CRITICAL: /NOUNLOAD required for stop() to work + nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL /FADEIN "$EXEDIR\splash.png" + + ; Install files + SetOutPath "$INSTDIR" + File /r "app\*.*" + + ; Create shortcuts + CreateDirectory "$SMPROGRAMS\MyApp" + CreateShortcut "$SMPROGRAMS\MyApp\MyApp.lnk" "$INSTDIR\myapp.exe" + + ; Close splash with fade out + nsSplashPNG::stop /FADEOUT +SectionEnd +``` + +#### Monitor Selection +```nsis +; Show on primary monitor (default) +nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL "$EXEDIR\splash.png" + +; Show on the monitor where the mouse cursor is +nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL /MONITOR CURRENT "$EXEDIR\splash.png" + +; Show on the second monitor (by 1-based index) +nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL /MONITOR 2 "$EXEDIR\splash.png" + +; Show on the monitor nearest to a known screen coordinate +; (e.g. where an application window was previously displayed) +nsSplashPNG::show /NOUNLOAD 0 /MONITOR POINT -1906 200 /FADEIN "$EXEDIR\splash.png" + +nsSplashPNG::stop /FADEOUT +``` + +> **Note on parameter order**: `/MONITOR POINT x y` must be placed **before** `/FADEIN`/`/FADEOUT`. +> The `/FADEIN` parser reads ahead one token to check for a custom step; if that token +> starts with `/`, it is requeued — but a subsequent `popstring()` in the loop will +> overwrite it before `/MONITOR` can be processed. + +#### Additional Examples + +See the `examples/` folder for more usage demonstrations: +- **example_basic.nsi** - Simple splash with auto-close timer +- **example_advanced.nsi** - Manual control with fade effects +- **example_formats.nsi** - Multi-format support (PNG, JPEG, BMP, GIF) +- **example_fade_speed.nsi** - Customizable fade speed demonstrations +- **example_nonblocking.nsi** - Non-blocking behavior with background processing + +## Technical Details + +### Implementation +- **Graphics Library**: WIC (Windows Imaging Component) via windowscodecs.lib +- **Image Format**: 32bppPBGRA (pre-multiplied alpha) for proper transparency +- **Window Type**: Layered windows with `WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW` +- **Transparency**: `UpdateLayeredWindow` with per-pixel alpha blending +- **Rendering**: Hardware-accelerated via `UpdateLayeredWindow` with `ULW_ALPHA` +- **Animation**: 30ms timer intervals for smooth fade (0-255 alpha range) + - Configurable step size (default: 15 steps = ~510ms duration) + - Duration formula: `(255 / step) × 30ms` +- **Threading**: Background window thread for non-blocking execution + - `show()` returns after fade-in completes + - Window stays open in background + - `stop()` waits for fade-out to complete before returning +- **Plugin State**: Requires `/NOUNLOAD` to maintain global variables (window handles, threads) between calls + +### Comparison with nsAdvsplash + +| Feature | nsAdvsplash | nsSplashPNG | +|----------------------|-------------|-----------------| +| PNG Alpha Support | Partial | Full | +| Fade Effects | Yes | Yes | +| Transparency Method | Color key | Per-pixel alpha | +| Graphics API | GDI | WIC | +| Window Type | Standard | Layered | +| Click-to-Close | Yes | Yes | +| Compilation Warnings | Some | None | + +### Supported Image Formats + +WIC automatically detects and decodes the following formats: +- **PNG** - Full alpha transparency support +- **JPEG/JPG** - No transparency (opaque) +- **BMP** - Alpha transparency if 32-bit, otherwise opaque +- **GIF** - Basic transparency support +- **TIFF** - Alpha transparency if supported by variant +- **ICO** - Icon format with alpha +- **WMP** - Windows Media Photo + +**Note**: Formats without alpha channels (JPEG, 24-bit BMP) will display as opaque images. + +### System Requirements +- Windows Vista or later (for layered window support) +- WIC - Windows Imaging Component (included with Windows Vista+) + +## API Reference + +### Exported Functions + +#### `show()` +Creates and displays a splash screen window in a background thread. + +**Parameters** (parsed from NSIS stack in reverse order): +1. Timeout in milliseconds (integer, 0 = manual close only) +2. `/FADEIN [step]` - Optional fade in with configurable step (1-255, default: 15) +3. `/FADEOUT [step]` - Optional auto fade out with configurable step (1-255, default: 15) +4. `/NOCANCEL` - Disable click-to-close +5. `/MONITOR ` - Monitor selection (see parameter docs); must appear **before** `/FADEIN` +6. `/NOUNLOAD` - **Required for stop() to work**: Keeps plugin in memory +7. Image path (string) - last parameter + +**Behavior**: +- Cleans up any previous window/thread +- Loads image with WIC (`IWICImagingFactory`, `IWICBitmapDecoder`) +- Supports PNG, JPEG, BMP, GIF, TIFF, ICO, WMP formats +- Converts to 32bppPBGRA format for proper alpha handling +- Creates centered topmost layered window on the selected monitor: + - `MONITOR_MODE_PRIMARY` (default) — `GetSystemMetrics(SM_CX/CYSCREEN)` + - `MONITOR_MODE_MOUSE` — `GetCursorPos` + `MonitorFromPoint` + - `MONITOR_MODE_INDEX` — `EnumDisplayMonitors` by 0-based index + - `MONITOR_MODE_POINT` — `MonitorFromPoint(x, y, MONITOR_DEFAULTTONEAREST)` +- Uses `UpdateLayeredWindow` for per-pixel alpha transparency +- Starts background thread with window message loop +- Applies fade in effect if `/FADEIN` specified (30ms timer with configurable steps) +- Sets auto-close timer if timeout > 0 +- Enables click-to-close unless `/NOCANCEL` specified +- **Returns control after fade-in completes** (non-blocking) +- Without `/NOUNLOAD`, plugin unloads and loses all state + +**Return value**: Pushes "success" to NSIS stack + +#### `stop()` +Closes the splash screen window with optional fade-out. + +**Parameters** (parsed from NSIS stack): +1. `/FADEOUT [step]` - Optional fade out with configurable step (1-255, default: 15) + +**Behavior**: +- If `/FADEOUT` specified: + - Starts fade-out timer (30ms intervals) + - Waits for fade to complete: `(255 / step) × 30ms` + - Destroys window after fade completes +- Otherwise: + - Immediately destroys window +- **Requires show() to have used /NOUNLOAD** or window handle will be NULL + +**Return value**: Pushes "success" to NSIS stack + +**Behavior**: +- Applies fade out effect if `/FADEOUT` specified (30ms timer, 15 steps) +- Destroys window and releases WIC resources +- Terminates splash thread and cleans up handles + +## Troubleshooting + +### Image not showing +- Verify image file path is correct and accessible +- Check that image file is in a supported format (PNG, JPEG, BMP, GIF, TIFF, ICO) +- Ensure NSIS can read the file at install time +- Verify WIC can decode the specific image file variant + +### No transparency +- Confirm PNG has alpha channel (not just RGB) +- Verify you're using the correct plugin DLL for your NSIS version +- Check Windows version supports layered windows (Vista+) + +### Fade effects not smooth +- Normal behavior - fade uses 30ms timer with 15-step alpha increments +- Total fade duration ≈ 450-500ms + +## License + +Created for NSIS installer system. + +## Credits + +- NSIS Plugin API +- WIC - Windows Imaging Component +- Windows Layered Windows API (`UpdateLayeredWindow`) +- Build system inspired by nsProcess plugin + +--- + +*See [README_IT.md](README_IT.md) for the Italian version.* + +## Version History + +### 1.0.0 (Current) +- Full PNG alpha transparency support via WIC +- Per-pixel alpha blending with `UpdateLayeredWindow` +- Fade in/out effects (30ms, 15 steps) +- Auto-close timer with configurable timeout +- Click-to-close with optional disable (`/NOCANCEL`) +- Blocks installer until splash closes +- x86-unicode, x64-unicode, and x86-ansi builds +- Zero compilation warnings with guarded macro definitions +- Optimized build system with parallel compilation support diff --git a/README_IT.md b/README_IT.md new file mode 100644 index 0000000..ac2bf8f --- /dev/null +++ b/README_IT.md @@ -0,0 +1,211 @@ +# nsSplashPNG - Plugin NSIS per Splash Screen PNG + +Plugin NSIS moderno che visualizza splash screen con supporto completo alla trasparenza alfa tramite WIC (Windows Imaging Component). + +## Funzionalità + +- **Formati Immagine Multipli**: PNG, JPEG, BMP, GIF, TIFF, ICO tramite WIC decoder +- **Trasparenza Alfa Completa**: Supporto canale alfa per PNG/GIF tramite WIC e layered windows +- **Velocità Fade Configurabile**: Velocità fade in/out personalizzabile (1-255 step, durata 30ms-7650ms) +- **Esecuzione Non Bloccante**: Lo script continua dopo il fade-in mentre lo splash rimane visibile +- **Timer Auto-Close**: Chiusura automatica configurabile dopo un numero specificato di millisecondi +- **Click per Chiudere**: Interazione utente opzionale per chiudere lo splash +- **No Cancel**: Impedisce all'utente di chiudere lo splash durante operazioni critiche +- **Controllo Manuale**: Show/stop programmabile con effetti fade +- **Supporto Multi-Monitor**: Centra lo splash sul monitor primario, corrente (mouse), per indice, o il più vicino a una coordinata +- **Zero Warning**: Compilazione pulita con Visual Studio 2022 + +## Installazione + +1. Copia `nssplashpng.dll` nella cartella dei plugin NSIS: + - `x86-unicode`: Per build NSIS Unicode a 32-bit + - `x64-unicode`: Per build NSIS Unicode a 64-bit + - `x86-ansi`: Per build NSIS ANSI a 32-bit + +2. Includi la cartella del plugin nello script: + ```nsis + !addplugindir "path\to\plugins\x86-unicode" + ``` + +## Compilazione dai Sorgenti + +### Prerequisiti +- Visual Studio 2022 o successivo con strumenti C++ +- Python 3.x + +### Passaggi + +```powershell +cd nsSplashPNG +python build_plugin.py # Compila tutte le architetture +python build_plugin.py --config x86-unicode # Solo un'architettura (x86-ansi|x86-unicode|x64-unicode|all) +python build_plugin.py --vs-version 2026 # Versione VS specifica (2022|2026|auto) +python build_plugin.py --clean # Pulizia dist/ prima della build +python build_plugin.py --install-dir "C:\NSIS\Plugins" # Copia in directory NSIS aggiuntiva +python build_plugin.py --verbosity minimal # Verbosita MSBuild (quiet|minimal|normal|detailed|diagnostic) +``` + +Lo script: +- Compila per x86-unicode, x64-unicode e x86-ansi +- Copia le DLL in `dist/{x86-unicode|amd64-unicode|x86-ansi}/` +- Pulisce i file intermedi dalla cartella `src/Build/` +- Supporta build parallele con ottimizzazione automatica CPU + +## Utilizzo + +### Importante: Gestione Memoria del Plugin + +**CRITICO**: Usare sempre il flag `/NOUNLOAD` alla prima chiamata a `show` per mantenere il plugin caricato in memoria. Senza questo, NSIS scarica il plugin dopo ogni chiamata, perdendo tutto lo stato (handle finestra, thread, ecc.), causando il fallimento di `stop`. + +```nsis +; Corretto - mantiene il plugin caricato +nssplashpng::show /NOUNLOAD 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +nssplashpng::stop /FADEOUT + +; Sbagliato - plugin scaricato dopo show, stop fallirà +nssplashpng::show 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +nssplashpng::stop /FADEOUT +``` + +### Sintassi + +```nsis +nsSplashPNG::show [/NOUNLOAD] [/FADEIN [step]] [/FADEOUT [step]] [/NOCANCEL] [/MONITOR ] +nsSplashPNG::stop [/FADEOUT [step]] +``` + +**Nota**: Il timeout deve essere specificato subito dopo `/NOUNLOAD` (se presente), seguito da flag opzionali, con il percorso immagine come ultimo parametro. + +### Parametri + +#### Funzione show +- `/NOUNLOAD` - **Richiesto perché stop() funzioni**: Mantiene il plugin in memoria tra le chiamate +- `/FADEIN [step]` - Abilita effetto fade in. Step opzionale (1-255) controlla la velocità (default: 15) + - Step più alto = fade più veloce (255=istantaneo, 51=veloce, 15=normale, 5=lento) + - Formula durata: `(255 / step) × 30ms` +- `/FADEOUT [step]` - Abilita effetto fade out alla chiusura automatica tramite timer (step opzionale) +- `/NOCANCEL` - Disabilita click per chiudere (lo splash può essere chiuso solo programmaticamente o dal timer) +- `/MONITOR ` - Seleziona il monitor su cui centrare lo splash (default: monitor primario) + - `PRIMARY` - Monitor primario (default) + - `CURRENT` o `MOUSE` - Monitor dove si trova il cursore del mouse + - `1`, `2`, `3`, ... - Monitor per indice 1-based (ordine di enumerazione) + - `POINT x y` - Monitor più vicino alle coordinate schermo fornite +- `` - Timer auto-close in millisecondi (0 = solo chiusura manuale) +- `` - Percorso al file immagine (PNG, JPEG, BMP, GIF, TIFF, ICO) + +#### Funzione stop +- `/FADEOUT [step]` - Abilita effetto fade out alla chiusura. Step opzionale (1-255) controlla la velocità (default: 15) + +### Esempi + +#### Utilizzo Base +```nsis +; Mostra splash per 3 secondi (PNG con alfa) - /NOUNLOAD non necessario per solo auto-close +nsSplashPNG::show 3000 "$EXEDIR\splash.png" + +; Mostra splash JPEG (opaco, senza trasparenza) +nsSplashPNG::show 3000 "$EXEDIR\splash.jpg" +``` + +#### Effetti Fade +```nsis +; Velocità fade default (step 15, ~510ms) +nsSplashPNG::show 3000 /FADEIN /FADEOUT "$EXEDIR\splash.png" + +; Fade veloce (step 51, ~150ms) +nsSplashPNG::show 3000 /FADEIN 51 /FADEOUT 51 "$EXEDIR\splash.png" + +; Fade lento (step 5, ~1530ms) +nsSplashPNG::show 5000 /FADEIN 5 /FADEOUT 5 "$EXEDIR\splash.png" +``` + +#### Controllo Manuale +```nsis +; CRITICO: Usare /NOUNLOAD quando si usa stop() per chiudere manualmente +nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL "$EXEDIR\splash.png" + +; ... operazioni di installazione ... + +; Chiudi con fade out +nsSplashPNG::stop /FADEOUT +``` + +## Dettagli Tecnici + +### Implementazione +- **Libreria Grafica**: WIC (Windows Imaging Component) tramite windowscodecs.lib +- **Formato Immagine**: 32bppPBGRA (alfa pre-moltiplicato) per corretta trasparenza +- **Tipo Finestra**: Layered window con `WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW` +- **Trasparenza**: `UpdateLayeredWindow` con blending alfa per pixel +- **Rendering**: Accelerato hardware tramite `UpdateLayeredWindow` con `ULW_ALPHA` +- **Animazione**: Timer da 30ms per fade fluido (range alfa 0-255) +- **Threading**: Thread finestra in background per esecuzione non bloccante + +### Confronto con nsAdvsplash + +| Funzionalità | nsAdvsplash | nsSplashPNG | +|--------------|-------------|-------------| +| Supporto Alfa PNG | Parziale | Completo | +| Effetti Fade | Sì | Sì | +| Metodo Trasparenza | Color key | Alfa per pixel | +| API Grafica | GDI | WIC | +| Tipo Finestra | Standard | Layered | +| Click per Chiudere | Sì | Sì | +| Warning Compilazione | Alcuni | Nessuno | + +### Formati Immagine Supportati + +WIC rileva e decodifica automaticamente i seguenti formati: +- **PNG** - Supporto completo trasparenza alfa +- **JPEG/JPG** - Senza trasparenza (opaco) +- **BMP** - Trasparenza alfa se a 32-bit, altrimenti opaco +- **GIF** - Supporto trasparenza base +- **TIFF** - Trasparenza alfa se supportata dalla variante +- **ICO** - Formato icona con alfa +- **WMP** - Windows Media Photo + +### Requisiti di Sistema +- Windows Vista o successivo (per supporto layered window) +- WIC - Windows Imaging Component (incluso da Windows Vista in poi) + +## Risoluzione Problemi + +### Immagine non visibile +- Verificare che il percorso del file immagine sia corretto e accessibile +- Controllare che il file sia in un formato supportato +- Verificare che WIC possa decodificare il file immagine specifico + +### Nessuna trasparenza +- Confermare che il PNG abbia canale alfa (non solo RGB) +- Verificare di usare la DLL plugin corretta per la versione NSIS + +### Effetti fade non fluidi +- Comportamento normale - il fade usa timer 30ms con incrementi alfa di 15 step +- Durata totale fade ≈ 450-500ms + +## Licenza + +Creato per il sistema di installazione NSIS. + +## Crediti + +- NSIS Plugin API +- WIC - Windows Imaging Component +- Windows Layered Windows API (`UpdateLayeredWindow`) +- Sistema di build ispirato al plugin nsProcess + +## Cronologia Versioni + +### 1.0.0 (Corrente) +- Supporto trasparenza alfa PNG completo tramite WIC +- Blending alfa per pixel con `UpdateLayeredWindow` +- Effetti fade in/out (30ms, 15 step) +- Timer auto-close con timeout configurabile +- Click per chiudere con disabilitazione opzionale (`/NOCANCEL`) +- Build x86-unicode, x64-unicode e x86-ansi +- Zero warning di compilazione +- Sistema di build ottimizzato con compilazione parallela + +--- + +*See [README.md](README.md) for the English version.* diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7c4588c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policy + +## Versioni Supportate + +Solo l'ultima versione minor riceve fix di security. + +| Versione | Supporto | +|----------|----------| +| Latest minor (es. 1.0.0) | ✅ | +| Versioni precedenti | ❌ | + +## Reporting di una Vulnerabilità + +**Non aprire issue pubbliche per vulnerabilità di security.** + +Per segnalare una vulnerabilità: + +1. **Preferito**: usa la funzione [Security Advisory privato](https://gitea.emulab.it/Simone/nsis-plugin-nssplashpng/security/advisories/new) di Gitea +2. **Alternativa**: invia email a `security@example.com` (sostituire con email reale prima del primo release) + +Includi: +- Descrizione della vulnerabilità +- Passi per riprodurla +- Versione/i affette +- Possibile mitigazione (se nota) + +## Tempo di Risposta + +- Acknowledgment iniziale: entro 7 giorni +- Valutazione: entro 14 giorni +- Patch (se confermata): tempistiche dipendono dalla severità + +## Hardening + +Questo plugin gira con i permessi del processo NSIS chiamante (tipicamente +l'utente che esegue l'installer). Non richiede privilegi elevati per la +compilazione né per l'esecuzione, salvo dove esplicitamente documentato. + +DLL distribuite tramite Releases sono compilate da CI GitHub Actions con +toolchain pinnata. Verificare hash SHA256 pubblicati nelle release. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..5e3cadf --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/build_plugin.py b/build_plugin.py new file mode 100644 index 0000000..857efbe --- /dev/null +++ b/build_plugin.py @@ -0,0 +1,679 @@ +#!/usr/bin/env python3 +""" +Build script for nsSplashPNG plugin - All configurations +Supports multiple build configurations with flexible parameters +""" + +import argparse +import errno +import multiprocessing +import os +import shutil +import stat +import subprocess +import sys +import threading +import time +import xml.etree.ElementTree as ET +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path +from typing import List, Optional, Tuple + +# Ensure stdout/stderr handle Unicode on Windows CI (cp1252 default breaks non-ASCII) +if sys.stdout.encoding and sys.stdout.encoding.lower() not in ('utf-8', 'utf_8'): + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + +# --------------------------------------------------------------------------- +# Filesystem helpers +# --------------------------------------------------------------------------- + +def _on_rmtree_error(func, path, exc_info): + if func in (os.unlink, os.rmdir) and exc_info[1].errno == errno.EACCES: + try: + os.chmod(path, stat.S_IWRITE) + func(path) + return + except Exception: + pass + for i in range(5): + try: + time.sleep(0.1 * (i + 1)) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.unlink(path) + return + except Exception: + pass + raise exc_info[1] + + +def robust_rmtree(path): + if not os.path.exists(path): + return + try: + shutil.rmtree(path, onexc=_on_rmtree_error) + except TypeError: + try: + shutil.rmtree(path, onerror=_on_rmtree_error) + except Exception: + pass + except Exception: + try: + shutil.rmtree(path, ignore_errors=True) + except Exception: + pass + + +# --------------------------------------------------------------------------- +# Colors & Spinner +# --------------------------------------------------------------------------- + +class Colors: + CYAN = "\033[36m" + GREEN = "\033[32m" + YELLOW = "\033[33m" + RED = "\033[31m" + GRAY = "\033[90m" + BLUE = "\033[34m" + RESET = "\033[0m" + BOLD = "\033[1m" + BRIGHT_GREEN = "\033[92m" + BRIGHT_CYAN = "\033[96m" + BRIGHT_WHITE = "\033[97m" + BRIGHT_YELLOW = "\033[93m" + BRIGHT_RED = "\033[91m" + + +class Spinner: + def __init__(self, message: str = "Building...", delay: float = 0.1, total: int = 0): + self.spinner = ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f'] + self.delay = delay + self.message = message + self.running = False + self.thread = None + self.start_time = time.time() + + def update(self, current=None): + pass + + def _spin(self): + idx = 0 + _block = '\u28ff' + while self.running: + elapsed = time.time() - self.start_time + n_blocks = int(elapsed // 2) + time_blocks = f"{Colors.YELLOW}{_block * n_blocks}{Colors.RESET}" + spin_char = f"{Colors.YELLOW}{self.spinner[idx % len(self.spinner)]}{Colors.RESET}" + msg = f"{Colors.BOLD}{Colors.CYAN}{self.message}{Colors.RESET}" + time_str = f"{Colors.GREEN}{int(elapsed)}s{Colors.RESET}" + sys.stdout.write(f"\r{msg} {time_str} {time_blocks}{spin_char} ") + sys.stdout.flush() + idx += 1 + time.sleep(self.delay) + sys.stdout.write("\r" + " " * (len(self.message) + 80) + "\r") + sys.stdout.flush() + + def __enter__(self): + if sys.stdout.isatty(): + self.running = True + self.thread = threading.Thread(target=self._spin, daemon=True) + self.thread.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.running: + self.running = False + if self.thread: + self.thread.join() + + +# --------------------------------------------------------------------------- +# Build configuration +# --------------------------------------------------------------------------- + +class BuildConfig: + def __init__(self, name: str, config: str, platform: str, dest_dir: str): + self.name = name + self.config = config + self.platform = platform + self.dest_dir = dest_dir + + +CONFIGS = { + 'x86-ansi': BuildConfig('x86-ansi', 'Release', 'Win32', 'x86-ansi'), + 'x86-unicode': BuildConfig('x86-unicode', 'Release Unicode', 'Win32', 'x86-unicode'), + 'x64-unicode': BuildConfig('x64-unicode', 'Release Unicode', 'x64', 'amd64-unicode'), +} + +DLL_NAME = 'nsSplashPNG.dll' +_VSWHERE = Path(r'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe') +_VS_RANGES = {'2026': '[18.0,19.0)', '2022': '[17.0,18.0)'} +_VS_TOOLSETS = {'2026': 'v145', '2022': 'v143'} + + +# --------------------------------------------------------------------------- +# MSBuild discovery +# --------------------------------------------------------------------------- + +def _find_msbuild_vswhere(vs_version: str) -> Optional[Tuple[Path, str, str]]: + if not _VSWHERE.exists(): + return None + to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version] + for ver in to_try: + if ver not in _VS_RANGES: + continue + try: + result = subprocess.run( + [str(_VSWHERE), '-version', _VS_RANGES[ver], '-latest', + '-requires', 'Microsoft.Component.MSBuild', + '-find', r'MSBuild\**\Bin\MSBuild.exe'], + capture_output=True, text=True, timeout=15, + ) + if result.returncode == 0: + lines = [ln.strip() for ln in result.stdout.splitlines() if ln.strip()] + if lines: + p = Path(lines[0]) + if p.exists(): + return p, _VS_TOOLSETS[ver], ver + except Exception: + pass + return None + + +def find_msbuild(vs_version: str = 'auto') -> Optional[Tuple[Path, str, str]]: + result = _find_msbuild_vswhere(vs_version) + if result: + return result + # Fallback: well-known paths + to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version] + for ver in to_try: + for edition in ('Community', 'Professional', 'Enterprise', 'BuildTools'): + p = Path(rf'C:\Program Files\Microsoft Visual Studio\{ver}\{edition}\MSBuild\Current\Bin\MSBuild.exe') + if p.exists(): + return p, _VS_TOOLSETS[ver], ver + return None + + +# --------------------------------------------------------------------------- +# Project paths & version +# --------------------------------------------------------------------------- + +def get_project_paths() -> Tuple[Path, Path, Path]: + script_dir = Path(__file__).parent.absolute() + project_dir = script_dir / 'src' + project_file = project_dir / 'nsSplashPNG.vcxproj' + dist_dir = script_dir / 'dist' + return project_dir, project_file, dist_dir + + +def get_plugins_dir() -> Optional[Path]: + """Return workspace-level plugins/ dir (4 levels above src/modules//).""" + p = Path(__file__).parent.parent.parent.parent / 'plugins' + return p if p.is_dir() else None + + +def read_version() -> str: + vf = Path(__file__).parent / 'VERSION' + try: + return vf.read_text(encoding='utf-8-sig').strip() + except Exception: + return '0.0.0' + + +# --------------------------------------------------------------------------- +# CPU info & build optimizations +# --------------------------------------------------------------------------- + +def get_cpu_info() -> dict: + try: + import psutil + logical = psutil.cpu_count(logical=True) + physical = psutil.cpu_count(logical=False) + return {'logical_cores': logical, 'physical_cores': physical, + 'has_hyperthreading': logical > physical, 'has_psutil': True} + except ImportError: + n = multiprocessing.cpu_count() + return {'logical_cores': n, 'physical_cores': n, + 'has_hyperthreading': False, 'has_psutil': False} + + +def get_optimal_threads() -> int: + info = get_cpu_info() + return info['physical_cores'] if info['has_hyperthreading'] and info['physical_cores'] > 1 else info['logical_cores'] + + +def get_build_optimizations() -> List[str]: + return [ + '/p:BuildInParallel=true', + '/p:MultiProcessorCompilation=true', + '/p:PreferredToolArchitecture=x64', + '/p:UseSharedCompilation=true', + '/nodeReuse:true', + '/p:GenerateResourceUsePreserializedResources=true', + ] + + +def get_memory_optimizations() -> List[str]: + try: + import psutil + gb = psutil.virtual_memory().total / (1024 ** 3) + if gb >= 16: + return ['/p:DisableFastUpToDateCheck=false', '/p:BuildProjectReferences=true', + '/p:UseCommonOutputDirectory=false'] + elif gb >= 8: + return ['/p:DisableFastUpToDateCheck=false'] + return [] + except ImportError: + return ['/p:DisableFastUpToDateCheck=false'] + + +def print_cpu_info(use_parallel: bool, use_optimizations: bool = True) -> None: + if not use_parallel: + print(f"{Colors.CYAN}Build mode: {Colors.RESET} {Colors.BOLD}Single-threaded{Colors.RESET}") + if use_optimizations: + print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.GREEN}ENABLED{Colors.RESET} {Colors.GRAY}(memory, caching){Colors.RESET}") + return + + info = get_cpu_info() + optimal = get_optimal_threads() + print(f"{Colors.CYAN}Build mode: {Colors.RESET} {Colors.BRIGHT_WHITE}{'Parallel' if use_parallel else 'Sequential'}{Colors.RESET}") + print(f"{Colors.CYAN}Logical cores: {Colors.RESET} {Colors.BRIGHT_WHITE}{info['logical_cores']}{Colors.RESET}") + print(f"{Colors.CYAN}Physical cores: {Colors.RESET} {Colors.BRIGHT_WHITE}{info['physical_cores']}{Colors.RESET}") + if info['has_hyperthreading']: + print(f"{Colors.CYAN}Hyperthreading: {Colors.RESET} {Colors.BRIGHT_GREEN}ENABLED{Colors.RESET}") + print(f"{Colors.CYAN}Optimal threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET} {Colors.GRAY}(using physical cores){Colors.RESET}") + else: + print(f"{Colors.CYAN}Hyperthreading: {Colors.RESET} {Colors.GRAY}NOT AVAILABLE{Colors.RESET}") + print(f"{Colors.CYAN}Optimal threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET}") + print(f"{Colors.CYAN}MSBuild threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET}") + if use_optimizations: + print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.BRIGHT_GREEN}ENABLED{Colors.RESET} {Colors.GRAY}(parallel, memory, caching){Colors.RESET}") + try: + import psutil + gb = psutil.virtual_memory().total / (1024 ** 3) + print(f"{Colors.CYAN}Available memory: {Colors.RESET} {Colors.BRIGHT_WHITE}{gb:.1f} GB{Colors.RESET}") + except ImportError: + print(f"{Colors.CYAN}Available memory: {Colors.RESET} {Colors.GRAY}Unknown (install psutil){Colors.RESET}") + else: + print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.BRIGHT_RED}DISABLED{Colors.RESET}") + if not info['has_psutil']: + print(f"{Colors.GRAY}Note: Install 'psutil' for detailed CPU/memory info (pip install psutil){Colors.RESET}") + print() + + +# --------------------------------------------------------------------------- +# Build helpers +# --------------------------------------------------------------------------- + +def format_time(seconds: float) -> str: + if seconds < 60: + return f"{seconds:.2f}s" + elif seconds < 3600: + return f"{int(seconds // 60)}m {seconds % 60:.1f}s" + return f"{int(seconds // 3600)}h {int((seconds % 3600) // 60)}m {seconds % 60:.0f}s" + + +def build_configuration( + msbuild_path: Path, + project_file: Path, + config: BuildConfig, + platform_toolset: str, + *, + rebuild: bool = True, + verbosity: str = 'quiet', + parallel: bool = True, + optimizations: bool = True, + counter: str = "", + capture_output: bool = False, +) -> Tuple[bool, float, str]: + optimal = get_optimal_threads() + cmd = [ + str(msbuild_path), + str(project_file), + f'/t:{"Rebuild" if rebuild else "Build"}', + f'/p:Configuration={config.config}', + f'/p:Platform={config.platform}', + f'/p:OutDir=Build\\{config.name}\\', + f'/p:IntDir=Build\\{config.name}\\obj\\', + '/p:WindowsTargetPlatformVersion=10.0', + f'/p:PlatformToolset={platform_toolset}', + f'/v:{verbosity}', + ] + if parallel: + cmd += [f'/maxcpucount:{optimal}', '/p:UseMultiToolTask=true', f'/p:CL_MPCount={optimal}'] + cmd += ['/p:Optimization=MaxSpeed', '/p:ExceptionHandling=Sync'] + if optimizations: + cmd += get_build_optimizations() + get_memory_optimizations() + + if not capture_output: + color = Colors.BRIGHT_YELLOW + print(f"\n{color}{'='*60}{Colors.RESET}") + label = f"[{counter}] " if counter else "" + print(f"{color}Building {Colors.BRIGHT_WHITE}{config.name:15s}{Colors.RESET} {color}{label}({'rebuild' if rebuild else 'incremental'}){Colors.RESET}") + print(f"{color}{'='*60}{Colors.RESET}") + + start = time.time() + try: + if capture_output: + result = subprocess.run(cmd, check=False, capture_output=True, text=True) + return result.returncode == 0, time.time() - start, result.stdout + result.stderr + else: + result = subprocess.run(cmd, check=False) + return result.returncode == 0, time.time() - start, "" + except Exception as e: + elapsed = time.time() - start + msg = f"ERROR: Build failed with exception: {e}" + if capture_output: + return False, elapsed, msg + print(msg) + return False, elapsed, "" + + +def copy_output(project_dir: Path, dist_dir: Path, config: BuildConfig) -> Tuple[bool, int, Optional[Path]]: + output_file = project_dir / 'Build' / config.name / DLL_NAME + if not output_file.exists(): + print(f"ERROR: DLL not found: {output_file}") + return False, 0, None + dest = dist_dir / config.dest_dir + dest.mkdir(parents=True, exist_ok=True) + try: + dst = dest / DLL_NAME + shutil.copy2(output_file, dst) + return True, output_file.stat().st_size, dst + except Exception as e: + print(f"ERROR: Failed to copy {config.name}: {e}") + return False, 0, None + + +def copy_to_plugins(dist_dir: Path, config: BuildConfig, plugins_dir: Path) -> Optional[Path]: + """Copy DLL from dist// to plugins//.""" + src = dist_dir / config.dest_dir / DLL_NAME + if not src.exists(): + return None + dst_dir = plugins_dir / config.dest_dir + dst_dir.mkdir(parents=True, exist_ok=True) + dst = dst_dir / DLL_NAME + shutil.copy2(src, dst) + return dst + + +def clean_build_artifacts(project_dir: Path, configs: List[BuildConfig]) -> None: + print(f"\n{Colors.CYAN}Cleaning build artifacts...{Colors.RESET}") + build_base = project_dir / 'Build' + if not build_base.exists(): + return + for cfg in configs: + d = build_base / cfg.name + if d.exists(): + try: + robust_rmtree(d) + print(f" {Colors.GRAY}- Cleaned:{Colors.RESET} {cfg.name}") + except Exception as e: + print(f" {Colors.RED}- Failed to clean {cfg.name}: {e}{Colors.RESET}") + try: + if build_base.exists() and not any(build_base.iterdir()): + build_base.rmdir() + except Exception: + pass + print(f"{Colors.BRIGHT_GREEN}Build artifacts cleaned successfully.{Colors.RESET}") + + +def print_available_configurations(project_file: Path) -> None: + try: + tree = ET.parse(project_file) + root = tree.getroot() + ns = {'ms': 'http://schemas.microsoft.com/developer/msbuild/2003'} + configs = [] + for ig in root.findall('.//ms:ItemGroup', ns): + for pc in ig.findall('ms:ProjectConfiguration', ns): + inc = pc.get('Include', '') + parts = inc.split('|') + if len(parts) == 2: + configs.append(tuple(parts)) + print("Available configurations in project:") + print("-" * 60) + from collections import defaultdict + grouped: dict = defaultdict(list) + for cfg, plat in configs: + grouped[cfg].append(plat) + for cfg_name in sorted(grouped): + print(f" {cfg_name:25s} - {', '.join(sorted(grouped[cfg_name]))}") + print(f"\nTotal: {len(configs)} configuration(s)\n") + except Exception as e: + print(f"Could not parse project file: {e}") + + +_print_lock = threading.Lock() + + +def _build_configs_parallel( + msbuild_path: Path, + project_file: Path, + configs: List[BuildConfig], + platform_toolset: str, + *, + rebuild: bool, + verbosity: str, + parallel: bool, + optimizations: bool, + project_dir: Path, + dist_dir: Path, +) -> list: + n = len(configs) + print(f"\nParallel: launching {n} builds simultaneously...") + print("=" * 50) + total_start = time.time() + results_by_idx: dict = {} + idx_lock = threading.Lock() + + def _build_one(idx: int, config: BuildConfig): + import contextlib + import io as _io + success, build_time, captured = build_configuration( + msbuild_path, project_file, config, platform_toolset, + rebuild=rebuild, verbosity=verbosity, + parallel=parallel, optimizations=optimizations, + capture_output=True, + ) + copy_buf = _io.StringIO() + if success: + with contextlib.redirect_stdout(copy_buf): + copy_ok, file_size, dest_path = copy_output(project_dir, dist_dir, config) + else: + copy_ok, file_size, dest_path = False, 0, None + + with _print_lock: + sys.stdout.write("\r" + " " * 80 + "\r") + sys.stdout.flush() + tag = "OK" if (success and copy_ok) else "FAILED" + size_str = f"{file_size:,} bytes" if file_size > 0 else "N/A" + copy_out = copy_buf.getvalue() + if copy_out.strip(): + print(copy_out.rstrip()) + if captured.strip(): + lines = [ln for ln in captured.splitlines() if "MSBuild" not in ln and "Copyright" not in ln] + filtered = "\n".join(lines).strip() + if filtered: + print(filtered) + color = Colors.BRIGHT_GREEN if (success and copy_ok) else Colors.RED + print(f"{color}[{tag}]{Colors.RESET} {config.name} ({format_time(build_time)}) {size_str}") + if dest_path: + print(f" -> {dest_path}") + + with idx_lock: + results_by_idx[idx] = (config, success and copy_ok, build_time, file_size, dest_path) + + with Spinner("Building configurations...", total=n) as s: + with ThreadPoolExecutor(max_workers=n) as executor: + futures = {executor.submit(_build_one, i, cfg): i for i, cfg in enumerate(configs)} + for fut in as_completed(futures): + s.update() + exc = fut.exception() + if exc: + idx = futures[fut] + with _print_lock: + print(f"ERROR: worker for config index {idx} raised: {exc}") + with idx_lock: + results_by_idx[idx] = (configs[idx], False, 0.0, 0, None) + + wall = time.time() - total_start + print(f"\nAll {n} configs finished in {format_time(wall)} (wall clock)") + return [results_by_idx[i] for i in range(n)] + + +# --------------------------------------------------------------------------- +# main +# --------------------------------------------------------------------------- + +def main() -> int: + parser = argparse.ArgumentParser(description='Build nsSplashPNG plugin for NSIS') + parser.add_argument('--configs', nargs='+', + choices=list(CONFIGS.keys()) + ['all'], default=None) + parser.add_argument('--config', # singular alias used by CI + choices=list(CONFIGS.keys()) + ['all'], default=None) + parser.add_argument('--rebuild', action='store_true', default=True) + parser.add_argument('--no-rebuild', action='store_false', dest='rebuild') + parser.add_argument('--parallel', action='store_true', default=True) + parser.add_argument('--no-parallel', action='store_false', dest='parallel') + parser.add_argument('--verbosity', + choices=['quiet', 'minimal', 'normal', 'detailed', 'diagnostic'], + default='quiet') + parser.add_argument('--clean', action='store_true', default=False) + parser.add_argument('--list', action='store_true') + parser.add_argument('--list-project', action='store_true') + parser.add_argument('--no-optimizations', action='store_true') + parser.add_argument('--vs-version', choices=['auto', '2026', '2022'], default='auto') + parser.add_argument('--final', action='store_true', default=False, + help='Force rebuild + clean (pre-release build)') + parser.add_argument('--install-dir', type=Path, default=None, + help='Copy built DLLs to this directory (in / subdirs)') + args = parser.parse_args() + + # Merge --config (singular) into --configs list + if args.config and not args.configs: + args.configs = [args.config] + + if args.final: + args.rebuild = True + args.clean = True + + project_dir, project_file, dist_dir = get_project_paths() + + if args.list_project: + print_available_configurations(project_file) + return 0 + + if args.list: + for name, config in CONFIGS.items(): + print(f" {name:15s} -> {config.config} / {config.platform}") + return 0 + + if not project_file.exists(): + print(f"ERROR: project file not found: {project_file}", file=sys.stderr) + return 3 + + msbuild_result = find_msbuild(args.vs_version) + if not msbuild_result: + print("ERROR: MSBuild not found. Install Visual Studio 2022 or 2026.", file=sys.stderr) + return 2 + msbuild_path, platform_toolset, vs_version_name = msbuild_result + + version = read_version() + + if not args.configs: + configs_to_build = list(CONFIGS.values()) + elif 'all' in args.configs: + configs_to_build = list(CONFIGS.values()) + else: + configs_to_build = [CONFIGS[n] for n in args.configs] + + # Banner + print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}{'='*60}") + print(f"Building nsSplashPNG plugin v{version} - {Colors.BRIGHT_WHITE}{len(configs_to_build)}{Colors.RESET} {Colors.BOLD}{Colors.BRIGHT_CYAN}configuration(s)") + print(f"{'='*60}{Colors.RESET}") + print(f"{Colors.CYAN}VS: {Colors.RESET} {Colors.BRIGHT_WHITE}{vs_version_name}{Colors.RESET} {Colors.GRAY}({platform_toolset}){Colors.RESET}") + print(f"{Colors.CYAN}MSBuild: {Colors.RESET} {Colors.BRIGHT_WHITE}{msbuild_path}{Colors.RESET}") + print(f"{Colors.CYAN}Project: {Colors.RESET} {Colors.BRIGHT_WHITE}{project_file}{Colors.RESET}") + print(f"{Colors.CYAN}Dist: {Colors.RESET} {Colors.BRIGHT_WHITE}{dist_dir}{Colors.RESET}") + print(f"{Colors.CYAN}Rebuild: {Colors.RESET} {Colors.BRIGHT_WHITE}{args.rebuild}{Colors.RESET}") + print(f"{Colors.CYAN}Verbosity: {Colors.RESET} {Colors.BRIGHT_WHITE}{args.verbosity}{Colors.RESET}") + print() + + use_optimizations = not args.no_optimizations + print_cpu_info(args.parallel, use_optimizations) + + build_results = [] + total_start = time.time() + + if args.parallel and len(configs_to_build) > 1: + build_results = _build_configs_parallel( + msbuild_path, project_file, configs_to_build, platform_toolset, + rebuild=args.rebuild, verbosity=args.verbosity, + parallel=True, optimizations=use_optimizations, + project_dir=project_dir, dist_dir=dist_dir, + ) + else: + for i, config in enumerate(configs_to_build, 1): + success, b_time, _ = build_configuration( + msbuild_path, project_file, config, platform_toolset, + rebuild=args.rebuild, verbosity=args.verbosity, + parallel=args.parallel, optimizations=use_optimizations, + counter=f"{i}/{len(configs_to_build)}", + ) + if success: + ok, size, path = copy_output(project_dir, dist_dir, config) + build_results.append((config, ok, b_time, size, path)) + else: + build_results.append((config, False, b_time, 0, None)) + + total_time = time.time() - total_start + all_success = all(res[1] for res in build_results) + + print(f"\n{Colors.BOLD}{Colors.BRIGHT_GREEN if all_success else Colors.RED}{'='*50}") + print("ALL BUILDS SUCCESSFUL!" if all_success else "SOME BUILDS FAILED!") + print(f"{'='*50}{Colors.RESET}") + + # Copy to workspace plugins/ dir if present + plugins_dir = get_plugins_dir() + plugins_copied = [] + if plugins_dir and all_success: + for cfg, ok, _, _, _ in build_results: + if ok: + dst = copy_to_plugins(dist_dir, cfg, plugins_dir) + if dst: + plugins_copied.append(str(dst)) + if plugins_copied: + print(f"\n{Colors.CYAN}Installed to plugins/:{Colors.RESET}") + for p in plugins_copied: + print(f" {Colors.GRAY}->{Colors.RESET} {p}") + + if args.install_dir and all_success: + print(f"\n{Colors.CYAN}Installing to {args.install_dir}...{Colors.RESET}") + for cfg, ok, _, _, path in build_results: + if ok and path: + dest = args.install_dir / cfg.dest_dir + dest.mkdir(parents=True, exist_ok=True) + shutil.copy2(path, dest / DLL_NAME) + print(f" {Colors.GRAY}- Installed:{Colors.RESET} {dest / DLL_NAME}") + + print(f"\n{Colors.BOLD}{Colors.BRIGHT_CYAN}{'-'*50}") + print(f"Build Summary - VS {vs_version_name} ({platform_toolset}):") + for cfg, ok, b_time, size, _ in build_results: + color = Colors.GREEN if ok else Colors.RED + tag = "OK " if ok else "FAIL" + print(f" {color}{tag}{Colors.RESET} {cfg.name:15s} - {format_time(b_time):8s} - {size:11,d} bytes") + print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}{'-'*50}{Colors.RESET}") + print(f"Total time: {format_time(total_time)}\n") + + if args.clean: + clean_build_artifacts(project_dir, configs_to_build) + + return 0 if all_success else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/dist/amd64-unicode/nsSplashPNG.dll b/dist/amd64-unicode/nsSplashPNG.dll new file mode 100644 index 0000000000000000000000000000000000000000..d13557d142b80b1ca795a3df6b602a6b9ec89564 GIT binary patch literal 113152 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⁡A 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 literal 0 HcmV?d00001 diff --git a/dist/x86-unicode/nsSplashPNG.dll b/dist/x86-unicode/nsSplashPNG.dll new file mode 100644 index 0000000000000000000000000000000000000000..0cf668f69832496636bea41661fe5cae8e4047ba GIT binary patch literal 94208 zcmd?SeS8$vy+1yiY?1{w%mN7pjS?g(HK5TYLR`=USQ3@shL{a2CRl~sb$y~R19(dy zak84pVOrXY_p7bmuN3`WytcQrwGw>XBs2+%5XCAAZDXZ+;-(rTCP8E7_x_yOO#)im z|9`y5%$f7>IiK_Nc{-nSw&d0wQlcbDcKn&9BsJkp{|dzOyB{u_Bu$+D!bIu0ac|6M zvMqaK#x23SHe{_^fB(O&|NgyMcYgoA`|ek=?pT|(UcE2tuKTi<++3D*@BM4mUXhwQ zK05}w0dne zSWOt;+w&gVG7<0emwr<7Bk_K;#t`p++hVD0OSmmn430~tlR_}lti zS7nn{B2Ne2Ybf)RHmOU1rjIfI_S&S)lO$i9=L*|7V`vkooSQHHeTA}i zvx1L%y6_l7TG-s>V>StVJvd`O7$BZ?b0lCi<$no z&E|4PDqCDqfxgJzL>);VhvNTGUOPFc_1wM-qwI1Y(3fQ2#!8(23RdIL&bZ`<{~IOb z`coKI)~s=xT>lY*2q@QRJXH=qiezI+c9yTgOkab#;^Esxy{oI(vh3U3hxnW+DE~LA zdDoK<+Fap?GRbGAJ0+W|J&7U!McTD@G{q%SD1uT#QhU>d;u!DZJ-f!c-tOHk$TWaRHorZ#$A+MrDicN0EMNL&b- zD#^x+cqS(8-E4F19*NyLzCDRPD2KLC;40U500V8IXp&t2B0fSGgw3d)rM`r`tT&W^ zE^_J1vNKug4#fCE)PI?XnAMm{;CyBt6-VUk_9fYZ&LWS$0w024R3nfpNe#=guWnnC z?Y5cedjQ@~^)#K`Gfh}SR?mcN=!9RMo%*wpRBbRpoks&mIcgXF3bkX%2hgmL6uKys zh?GaqE?YIGY@!@Kimaly!V&pL&*LK}>A84UJrL_9#E$mTFT_Wzmma~#KkTKnvAvXy zUdlSRm$K0xvk7{I&+>vOhbepy%^YMi-vJ-_9$WZSEZS$jOR1RP zI}u}LLd0f~QT>xOBBGzo^akWWH##xMu_fNkz%n^bSJEd%zdWO#mLYil7T`GyMRf!;uFjuXabOSjmw44L;SX zvz2)d80AeXLN+lU7^aWkXO=p#t{TPxL*{KV$~#t|$;@Gv&ZX$jQ4#ZY809amFv=q< zn1k{&Z^S4+v|BJ5?@POhy#VqGQEC>IqLt0OhcM+sHm%3O9G}aR?Ag>)!7M^i?Esi8 z%K1}~GeV2Q`!@5wG;jco z?{hSmlHu+2`OS2Qo5ZHrpv9=drjpA_BCJ{dwf7}ceU6nMYVJy4SNjaR0kBkXh3U^k zhm8Y0CV{|hvXVpLgGw^9m6|>WE9n^f(KPnsz}Sx=*1p>PMit)!85Wej^_=mo>v?Ki zoH)}EGq01Cr0qV2-rGg9P)|L9C#KrPm{~i-7%Jz*=QZ((AdNnn4HS1h7t0##!?+=T zJtECKiUs{axIKx&)V?x*MbH6}!wBEjZO-QyU5$Uu-L`tm?@e>N6eo-GLO#LsQg%W~t{i6tt zMe;i^tkzy86t9%ScT%A+eFW!&5c>E`4_L?n2imlaEu^lnu(x5_l(Tv0h?_+n_k1Zj zVxO&Aw@=cV6Q~nTv1GrVVjcx_O1@IbHVg{zS5r_)u~~O!n2>+?D0+W4$#mVgp@79%5-bMcSg#G|y<-lOpZX(X=07 zO2o?T6=~x~(!%cp`*;)jhNua1&;${KVMGM2rSnHyl|ik#5df`L6%c4@6_uscv;?!_ zJDN5!*HcD7e2tRUTo)j!cYIfQ!=Q@VlW6&1x=Z}ZB=m_vi0-Ck#;40-eBemG;g`R)J)F=0G3V&v+?>w{pz@int~1@6*Z=q%Js~m>BNgx zSFN&Gc$wW|Dwenrd~%8<*h1$dv@CWYhadcx(YoayqGicKyNHXJo&ONWk7>9pemR2U zuZcE;@dZ0UW#S^0#hIwzSZ@qydlG#DfE4amZ_gJpn6fe^wkg>xm58NLN7VDNfitY5 z`2%}ShhZ;SW7SCvFgAshJHMmzgAueb`n`D8?;5Azc4UPl@vY;80f2J$#wvaU6DY>m zA+Oc-xG*N8A{+R5nV+d?6+tpO3BGQGhp>b}9rQdJcHU3s3BhN zLu*Cz*mYr3uHTQ4aWg0e6+fH*j<@(POR3P4!FojnL5pXsoEs<1-)Ew}HUZxTCU!T$ zReoj^5fwt6Ii2!8hfx5AzAOO%530Q}fLNrIex-5|s0af5G@%mbWgDl+)9VodJVgcF zUQiAzXmOD|d<$U)as&y;F)?OU?aS0sNx2Tw7n*)tsv!ZLr4s*Pc{HluGUg=nK(G$2 zG2e-%#mkX2gcu;s=v zYhTS#69$gOknp6EJx;Z?c9S@TR$~@xTvFU*dds56Bzh#dBL&f1o(6MuG>Lx-Rv|wh ziB5;v%Tyg4oKFfwU0Mz7Igdn;;^Q(%SfH}SDTQ3YM^|F%-woL}B=N<75U2-xd>du> zwE)!M{qT3ThRToIy+eqC?Z&qSv_TI40Uzy2^g$el--E365v7E;f>H3TSb6*jJc7L> z7FFtvVrbn0z53>W{07c>xF)>(#E4 zl<9t6Vd1L4y61$d2;0jw?68^M32@LDlUxsw$+I9TH&%XZsvq)KDSN9s!b*DA=+Kw( zg-$-$8cN}xA?Xl$qKSIq`)I%}Djbe%v%4eFb;Y{{sQ#t8dNS4vJPU1W@OJ6ykkugx z>izs(YV-~a6ORH-t0`1Xyv#g=!Kg4GrG!ereGnbJ!sd0u)RnA{X2yaVqN4igp)~}g1WxQr4C`qi+6}<|N zvI;`xAXVB6EQrSK#)lQXgM^!S^!AbHDSwYe1Dtvp_`}RHA3^!5$27yojrzO9T?pY*x}kT3v4NqnQ%sF8(_SNHKal09DtM4ID$~!3u{ckk_P8Lg0<>?augj(j3$1neL+J zg-&|rIq132j%W4u^jz^u%@(fzhqFn=Jet9_>YrjA&q&Z-I?NT-<^fG z0h8lw`VOBEQeY5SZ7^HyqQu?V_-aqez>^SfPs$XjG(Ts>LLvLgyCYJ2?QDG0(zCvq zpgj7rq(OW)cS$U@0F;QngGq#@beJ~M_#nRgc%RG93c?aFzKB&3*UyR%zFuqH7TvexV(68y?7sFeBa zQM;#80cm2d^~%4Q2!hyqb)>3wudS+;-((d?;n`T?(F{Hh-#+fT2ABg*;?o;05} z#9BeCI{~vk62O{k<;7@ReMA*Gc0xBrIE7z_$mrjZnUlK8uf!)l&@lcXC z&`+8bH4aHlX-is(0ycgNk;QtX05SX35kNcv{S6=EQSQGGABcAEwTYEfyD)$vl47TC z2y70>4oTXuO}&H9wy?MqYx`mglluu1v_uXD39;L)PbKH-NHkYFXzHniubz6lc&-so z%BZK_BcAK`E6l3QW(%m)#cTx#X61$fmp z(FbictIPIJ01>IqZ^)qJ>o9XjMuZjESIU12az~6$xlG%fEp3?rfJE*0+M?5$w}-)G zTQ?tIBl6B>)=VTWmf`Aeuqqn5S;z`laxL}mICo?}&8mmvT}QnwDao}pPD!0il=Elk z5qe*XnG^_6mo^=Os5?rs{9|brb`Ib8UMNvWmF@QILukGss0wM&$1qGFjCy!G8l)Qy z^n9eAo=?=$^Y>fm`SNBwtG7S$lz17Biq{ilpQzp*UMF5p-y>eX3gY$nkHe4S#k_Vs zyb}R~0#72)F6t#Z#tMc)K0Qnf$?)zWOqK%^lmIgA-aRlV7PNbRNm7XJh1s!*2n&pk zdvxz$9C<;9h>RQFK@xir!lX&XtWq3kE*6^b8vPqxdzUrrb7 zfkI~i#dQC{r?2IvI>@|3u{l9)^7gLTEpTkH7Fy~`kUpE%GpjvmOAP(c_&6Vu>p#OF z0;oG;)EsjkER%o1POsXbr|6sP`ppi#0@mVVtmGtmsP5EV_pQBwdAsuNQ`X)yp@Vr( zglu_5YwuGhbgT?Xfo~R7^HKPc-s??yVIyt*dZNC;uHWR)%bcw8 z1oNKMD+hIN&p<0_aY_taf>v|Pwk2B>EsWuLPFe3sMg@5&du6_NXv>A}gXoV~R!SMu zAvGshdEYMTRhH_*Q|m}zdACsDgJG+WX|nC647$jmi_`&(2kn^t5UE;+-O33ht(@8i zpAospuu9DAw90ro;EoI(2U^q?__S=Iv!AeOg!Y%){DXvK+n~89ayO@Wprw zw6qR~1szn?g5KYnr98k=1(F?E0<{;gRo%IZ@Spj<*7dbjfXLTwiql12&-?0f)b>Ib zqEY3{mYIh=Eo!dkusU0m)WqB>(7KLWuq_~K1#DKR4TUnCgx&$O{*s4WT zIN;?HcD0n-zgb@i>brrpyAL*hhGKA6(LPSzy&1El?#TDBTQKnEOo8mcyE7LG)UBt8 zTVUHLYahRA)uW39tVnm{AO!sa{N*QgfZ7p@9Bb`AbO@A`SPR@6oGe)z99B}R`*A;(K*fW%ouzeWSeAlUc?p9sY_ouPK@Cb}yOwCH~WwL5C7qrIRgo z7#m#fBoa*gK3(m_+U51=6^NR=41EYDp&f9sA3FOB?A7Y6I%cTXg&AsKjxUDFIIP^o zN(PN0=%z(1v(B*BK~}-k$DT$Sdxt-UvHXp#p~MVFHm7YXjwUvinCvU42c|$|OI__3;ZKp|G|9ZI^O2QUDSroFB#b$NStKr8ik$->PBKv)dOp^V z4``cRiRy;B&1qRl|1nLOjKB;Ej6>jJ1k{zRI1__f%1Z6wx0Km2fn$ZaE`#3)Ffq~} zy-FxCJ$y0Z0?-84^61Ha?4V9#1sTi%04}IbY@K6N2|iboq<$@NnB`Dz93-QWn4nxO zoP5~4y6eQK&(O94+Y9QpTG(bFAh4AYAZ(Rn7~4pIOF`G@>s)M;oezNy>Fd(?{khiQ z@hBo#K{lq!L{ymXP@Jp)6G#05tFjWyQV4o9P0Ar*+t?56i7ny&&8e6GENv=tj!!(C z*o=W1S*6$2UB6=S;x6>v^(8msJ-+UGLRPsTI)!gTUE1ahX{&=P2%5ziw6O{Vs|l;k zF1FFmS5XiM9=;L?c6ZS8?0@j_aLUhP#~yQZOSV0HP_AEq<$PDfhJV_bEcv0o(14z) z#Z!Ke(es5%1S8up?)3kFgWK7ct%wq=Sk``v4At^|k!pEeL`pm&PfF3+UE0UJIY%rI z8+C6i`+Gm9>ag28Qlbi-H=kGb=#~j z_49#d478*;bhN>)b=wD;VdOWJtauB4j#LTkv&gQsJG5?xm9G9h2Gr_j2q5g1n8(Dj z?2PgizS7cQ!MqU}DDD%35m=*NLJ(0rm#uVQU}n3gv2{*uco>=nCESudSNn2Uad;r{%tZPX)S14}O?_k9acL(1L(?&T9W|-@o<~o;&X>_hdU(l)_&pjXx{vki%2dt^A6Ek* z(@Nr(BSDx4?e62zMgGt%bk2Wbp|W1Y_A8U&eL`S)LOkDO_wlk=+@}+$Y}C&F?C$sT z+}Zf5LdBibXRLKqYjECt$;Hn@uH6I!Zb85f%5;z#?c=`|#X+7@I8BP^-$2qwT0hCh zr%`%)l4up$i&U5>_M)h}A087=%J2c<&ma$a1YMbKCUqc9Ys=8uGqmB1^~a&1W*KxJSR#_o{WNyM|1m1NS&tI1%H^FL>+U3&RY_~7O_cekWzt&YT2 z7y-2}O=Y3R({9XPIaFi!WNb}BjGDsKp&LZz2mZXsv&p&Xe9t?ZrdmvSwZ6_-r8{(= zT`zXPfXeU7vBVhUTz6J5NE;vbuZ-EG-zlL|A?3MtX%*%wi1ZqLu+1+bz8f4P>>a`145Y)-Fo@wu)whbL=0h9ivJZe5xN_?fNWl(yjk8C z2%sd@r`5ocf8H8)A$>5&j0g0@HM;nUj)TWeyxKu@GL2>#8UG{b(V>wt@hlD!IPV=| zcMh3ds}5S+y%akn6>FH#F?DqiV#ES;Jz1b(QymgQ5>b^MfC}ym6@hxQ(7uK(6!<_V z^2D$K;fcZY4>8rrmOI>s`S)QjBd4&9Y34>JL3NoM$xO&TLp_ zc!E6|EM|?h*+b{;?nH;lmueU0LmOzrYcQCfoWv%}TKU2UymkEbM zCu?SJCboHoH#$6n@`F3ULP$h;@GUTEOTO4JA`{3D{s^(%9|FBG63bKIT+XW1tx&q! zo*|l|PK*l?@`GLAWwE@HtUZI01MS4#Zw4T0zW zr4W7Yzc1I=n2Vh_1dmqB*UIlXA^S|nR~LP?&=<)`_rF1(J@k2hma#2&!#-o9v-{UE z(1XUtLE{Fh<-1`6!7;GuYYc4q9D(hK`T=eEkg;*-U%=UZ7F_xo1D8HW;M(C|zkbST z`F`}6%h-sPZ9|W_qFjFAgKzfk2jAAa%x)|xzS8?-i?)^4++AdbB$ z=E(nIvNU%moPJvSYoeL`h$xnA+=9|O=y4nVg818lzt|kvLBPg_8<*M|ifpkM^f9Gd zag9q88j2F)d5yUfjRmr(wbYt^mUR5!PK;3u(=_%j5XpJ1ajDr*WG=s1`^2W67DX;X z5%wlfMY{H>UHi=b^=Y`nWIxgi)OJXPlFNa=4E)W;U%bx7rHKtiiDPgR-)G}x<+0+< zYRf=7Ed@osVfoF}ChJo@Wi6@o!MaatkzDr~JuzI#_*?G6!=HsmRW2UY1$f-&!(-#^ zcx+u4ldx|_wrv&2wrw@CZTmN5+jc*)ZQF!w+qSKX3-q;+F4P@p{`#5Pr&-!(C^1rv z5an;iI>{Pn?xK8u8fXTyF_m<54LXP=LqI3#oFdgI)InMM%mz7vngVei?r_$ck0yeo zur{p=ah(r5~=c&&u<>f|oH*i~xq5UKY+U^WP=nvm)sId`?I_pOD zZ`udb;q%yxnV`sQs(v16A#G4Zs6`#x2Z`2fUs`m7IuKV$ZXoS}=-WtpULdP(PG`<* zrL4s&HLh`L!+D}qGf#l|2arL-K*5UD(}C`8$kfDgOTj@Z414$(WHeA_ChgFUn4WgB zN<{sT7+1gq2#l<}0$zz@|g8DghZaEPydrRhpNa1OQ;+4<4dY2k1c|NbV7#lR#BKF_dAffyWBtJsRO)SdP$-nZ!{1qyk15(qr8qq(1UvqXqeW@k_?s2?j45qTMZqB|O`(vT zJI6FP9enxRUE?TV5?C|6%K`(B0ZwM0E4gX9k3#~4eU2;#<3YOS2OKg^VqnmHj__Nm zBR@knKBqb0fB*#%JV|SFCVmdL6;_j0fAE2<{uGyT!LE8S%}&xNu?{FOUH!tJCsaToIzF4?h@_S{|0Y;qEY1Vl}6|Fr`RCM!xSRdKNd^>3Et+WG>AEV-ZUVz zR&86FUGMJbgtui@)Hr41@QIa&bd`KvS3nOLE>qWAnLDq!;xk;Uid8!RBvbYvm- zTX%noJtVD?A8B2zS9ZlrRjf5S0b-9ZQBD4)#|2*q8wBmKIc`Poor0jo%1M;hu9a%+ z>^#OCB#X@KEO(OVNN08T^0Yl@JJoY(8^iMg_M+ zMT};|(|#(_td#emrNwbP#|#_uLk#=A{ zt@a8Zp9(NS%g3tHde&IF8+@Dq6514|UxSzPgJ=Y379o^D?=)%dYzav!lL(8H!6#-$uW9D5 zgWu*JNDKTTe6f`-J{9jaFSoG*ATtDKkNVR{wklIEr;60D`e zS+bqK21Om-GvP^;;O*7gTm|SLPl{}Oi8U8JiU|enHJVH$w`9;A(QB}~@^-vqo?CK)x4hmsh~q~5{AQ-RY@=X)QQfBY0Oc)U-_KPKMiKB$-U=9lb|>kWLd zvP`zf$#S)bUH5h|?=N-r1gV$3M$O)*@sFt|SWQ>Be{1#%bDrVd>*N1}E)bMmy%>d; zgFS}(xBV5SUu-THY7yw#*m>yjlK;Q%J?wIUm(CzXvb&zLYi9ZgRCeE zY*pKq39V_5O(Q%zfeWl+YqA`+?@uLBHiGpY%ZlG@69rhy>>sk;PH(Es^$SQNo* zP#QXl!xvy`@ntatmRJaAHQ7=buz;S2)tv3)uVP@SD%8YNU=yt12gEXkt%&^Gc+L57 zgU5W@0;=<%idwn;JWLXQ1^*QGLbsW}*`X|jSw{j5hsEqYMCPw^DDFCsuwduZd4xrK zrq#0e2!PI@aW1R{=#eC!HPf6R>qU=V(~G?n`5{uC#L%g5yXnDzVltla@l;S~Y^sXM zh#Av|>F+7$vQ~@iVlqN_F={$ulQAyTAEH@PGKeYDC#K902o!Kw|By8_MHm{f zwNp-dFwQE2=xsEL9kuzAa$kQ!Ez#K|@Im$aY_;9p?`c(Xjm3_}Y1d|`=hq)sCN~Cb z*RHgw6S3oieINJ(o@8y&B(d!SNF~j5o;WJ;w+3$?qB5`W-S zFCq}udP&OvL8dwp;DW4>77mt4l~tj7t@iJ(#PxIY|2=Kvm26Qa_IJQ_FOi?eJjGC8 z_z66TDl~Og#AJqdur#96t#k~Vg@MSxe{qK9R@RsvsO6Isu ztNU?uf~ofKt)K~Fua3VG)}N1ddb`Q zTb&yV?ru(FRW8ph8FpmdI*UHl6P{agAxEZdn?|3@q5V4Sz^aRtA7g1P01_Qni1ME0 z&h?QM*ookD69erv!%v#9Lm$MXpmy&|G+Z16O=7L*?ib>u9cDx&_N6<8Z4hlf#BTy6 z3-EDh4Nl(a#V$S!Lm(?TfbNNHA>Jsq5SPTa5GTjC5XZ%~5Y_K#H4zC?=5b{@#)7|w z@h1PM$@w)e#XLy;e9vELa9Tt5rvLL9jQ9R0faOlqB#3=hOg7|Ck~~o(5Ws24kfax9 z@&J01zn=zTBmP;Fi^X?iuMxYkx&L@KHeJ*p^UDC$eXvk3&SK3YS`xM^MhteCgw(0< z8iUD%rrc#GNQ9wfMv~pd7GD-VwETStxn@B|3~i5^rrfO!oAQsIp?uON`IK|1L(+8BD~RmTHm1RC?{)htFs#O}0bSXE6^l6qc9@ z59O259kIdYmmv;ytJh%W0Zj-0xdrjuaVIC^S-?29t$HCLU!2j(!&BmX58DdRV=71m zsilgpa~}_zTYo_I)}y4AvX^C}-d zDaaD^Q*|2IK1=$FVeu7NQ3M+S+un^P{0?42u<)MLT1dEqbwho2byZ_4vHj>|3p2DU zzQZj%e2F$<#~ZDsO>Y;G62h?{6FvnG*rle~phB{@&_22|H!^9o9(x(o|Cq%crOE|x z7tHMZUi1j~Mkmf-|EUtBp`1nQ&X9bsChfHzSSYLD#rcF$k-C&2eP5Wy7Ci^Pl723l2Tj8^!X6#z|12C}E+weRq zgP#X7X}{XoMEL@eukAS6|L65uD;k)Vq&RBc0L zqNY*g;`ouvtC9?#S_uNhbLW9kN>kBmydoXD%2-0%;=8x;_JE?3dg)-Mybm^C91U?B zs}ekil}w}XRc&yZQJQ5eeN`@c6Jhq6;m?wb-oZ9rb55Iz=Buv>8AjkC`X}D@OVLS~ zxac4<7-7E%nxdn!y8$S~!^;qk?F2bESv@g3MzM?Ghk+*SaHQaT{x`HF)|>{AR?(c* zv_(O50Jf|PcV)J62X&5&s=n644Nh5L6iq zQAwXs(pw5|Xbd0C8zOjjAk4^nC^lMW4;J?4NH=WSLvvT11kv2GEjd_c_b(AuChx6P-V0O4tZZh-ggWTJTVBp1mVqZgWv>&<|y=%UKmz30z$la2RoYc zraFOo_P8AOW2{!y)(k@3f=Z`7Nz?M{D%K*pUkp6$uz6Ica{jk^sj5zulk>qy){5wP z!kcyCs5dLaM%U{RrEtTO_HQ_ zn9=uWQ%@Ye*O%Hsq8)PhMF7-VvW-ReL{qdo90)r-o$41&ux&-kSd}B+wy8+Dt}Y*X zVnn&JF8@dL&Z^7*F})|%&B)B>9x42o?Z*;@_!lw z@*goE&m~wuevdbRT-Zd~Xmm;wDv_ejrZBydni}!aVT%emM!OV}WtC%9bTYNk{DFN; z0&NK|wRfk|8gv99oVj?ZzxC&eSd(grQNA!;b+b`hR~fJ|DKZj}z-oVQ}S8 zuN3>|BvYpLA<@&JOfBU<2gU)`TUJ&!%Ep2jptsTSbg#^WU^c@12`CBJ;ucQsEG0if zofNdgTM$($qCY@U?z1+`=_s5!nZJPM!`d*8 zM4?`U1BTbx<~>bS803oCWq40RfuPYaNWAbuIKf6I@;mU|f9(9oaHIk27I{GbO8Vl5&kkZeL49{{VyzZ45o<~~<&kuD)*&Ox zSVAa@1P8kE5a@S$1eR#fc=vwmbb>?uo;a{lmoH8wWHwo+5(+T}+5^hOjT5x}96;z8 zU@QDzM1AP3H%EJ`)c?)STH6bP+v^JsL0Fu#E4n5H<%jg5zEgq_zJ!;kJJLhEN(k(I5xXtwUK615&@ zj78CDH`;{v(-F4|=&;*Vo(u6MG_^|4Ic5H$!tC{r0vPP?xlOZ0Nu2zs5hu0b`PbE0yBA-rB z`A!1%RX{*#pm2L&RTb?piQO$Y=D>cH0rugl&}Kpr!F_S)77!KMD01kQ71DY2L3kP0 zUVpKgT7Piw#WA-21%`bhwllZ_(UI=Z;jbe=Q?o6aU>z`{0j%sEfUWk>A#I_9gkG4p zBCUQt?-IzI4_}TR16L-N{bQ63pt`Eo3O`4@I5Cxkjm)TXcNz-1&D~TUb8jtj`uS?g zAAp=&6`LpUJgRiK4?_yoYjP*r0QMOU!bjR!^)mm??xRRBPhdDP@ODrZgh>&h_OD>xJ$ zYCA@%7z+kU{(Mp@y+izY%IX~qBpz#=f@*O#i0`JTHtS@spI?l|BJxLw#Oq-#iMo%xCifF( zJUhu1OZqP>DI_(0N`{PEw*v_Fz@4X{ zxJ5dgU8DcK29XiMx7bHLi&_(BPR}v*)P!-a| ze^Bu_eFcShuL9?D;GZ@hnt%iIsCyqlNSEM{$X@fad5|<1j!J*-{_^PIqesXl0O?60 z`$oMGjLL`f8c@$cI4XFC*WbcYJl-MYb9W20`J)}ILqCu9wql!|y{IP!nZQac0}fp7 z_m95rZaLcF0bQ%?WtBZ@lE>Sp{2C_?*dT1((O;l4CDqTL2If}r0N_ZvOFVsc>*c`8 zfno`JP1A&448;*(hr_er=oW`R9p~^TEe_8?xlj7|qv&V;W8@vnvP$4^FC((~Ze^n87g=0O^HhGIsqIRY55MH+k#1m7qz`7P-Dy*8pa@R_~s zLClUHLEDF`EP}8ASW!O|QN$y3k-$SD;*f|CtRsL3C{}tN#Zi5NXV5ai!Ah3gD>pmT z^om!5P_Xl_17Es^UxpF^{W`1>DB{6A5DtQ~@ZcZJMm)}oUIiu`J6G`&r#Ltf6L_FD zPu3Q$1b&i|#E)Rc(&>sZ5K`t4gxLBbVc--%iPMY_pc`lbJllam+6Jg_6~{8ZNJ!8l zw*f>U9fw97c`v54N;Kj6wgJ$ zswN7eD9%{m$?qG5I|<26WKW>@TQM<+MTe&Hod^c{lWH+-5|oR_p5_dtz{wlN6_DNn zct^aQAjls=PvNefF^xq~w2!?>)|EwRQaf@=QrjX2{xb2GO}cx!rMoY3vKHjA={LH@ z+FR1c=HV35iAysZK68jA9NL!J^7EOd6A;c|SicNB;(;$5FXkRnt z3*J^0^QyGq0i1Tdq!#{Dn0^cB(D-ix4fj_mDJ(@O<9rQB1)&?mm$tdy<2#6oZ?GF1 z$aB}ve9Md&2_u1!#jF-NXlq(yBT=k~WGs@;Zt(j_K@(}hEL zY)cK;Cz z?vQy2`KiQHthY6v%3YY{!gK&h2dv4DnXhEZ(+hA%OKvrmsjJ#^l{&#X#YL-v%lnWt zE{6{P5HUPeyA@lI(_$`7)pp6(w$y>EZppJB$hE>)5Ti?VJ+sjAnC^mphAqylur#|% z4}eI9d`7qn!V(=HmJi{0h;_C(7lfChhQL3q4Cr}1d<;2_FeU2Z7_uJjqOfs-o{t=( z=M!(!^Y@+feEATbIP*LxUPh02J<%av;RE9JbVR&<^%7o>|2W)>7o(%j9076WxetMM zQDyARvmQPvGU32AAr97z7!g3R9t5nT&elze;%u`(d-Qno6uJw9`oZdk>6j#NT*rNr ztNGKAS7IV5hGlmy_*9Iz;ux_}aty5H6Zl;l-eWMc(FT{;FA&=rAiOj?k%?ahB;tZC zXk#WzfuuA6Yj-&kF!L^FdvC*_V4Sf}c@~sW^8t97kg1Lc_bb{E6E``K-F|ME@PZV8 z%H4u3!T@y?JDrfz)R#G!jdUn3#}%ME0IKyY@|Ohi=MULlIS&_K_~BW06#WSq3chrY zV*a3`==P4-{DIVkcJ-uH!`g)#n*kepBu=BGhu4YSzus4bJrKQB_kg4OYuFRNXZUNY zJuMr`<>x!cSoi97qAZr5E1HO#c3=&R&aU>fkqz+Mb4{e1dd6w{EhD96K8!?XhHRuY z($j%UGk-&=T)swhJvw=uybovswjF^XvJFT=nH>LsiR?e)*9kEDk4>i*P&pjk%cp<<6C`9bb&<0Dy#@-67Qmml3*Zh}k zb$L{hAaa6u`qenZKSJU=3u19Gx-|pxHbgC(D%YJMQA{sPzD6La zD#vZ8s$rz6P`oM-4?-X9fqfGaR$M&#E}&{0Xb0i%9s!yj2THWQ&`wa94Ukv;%aAV+ zkj31w_L#8!8d|Iu!@UZIiQ8d)LO^ezDULuOl+<2OOnrinYQ6X#@oHEP0|umEd}vQD zBW5Th{9c&upegjw{SRczA_1RY3T`b@v9n-?>@fZzlV#)UwKM>--SmQCVBW;n6z%bd zn>8i^Hi@EF5!T|woW=A|agU2R>dp|t%J@?5LI%RuT(^MJ2}6X+vH-N&DyohI7lm;wu^h5RoN1wgEV#Wrl6y=V7vs?;K z01|8vT#}?L*3NhSFy6R$2mL2v1T?ygYe+P}#~%hPqT%?&amSa@E%+LDm+GYXk=V=H z_)Q~#_?2jUaLxUa>lf4_ekscCj`g;6iBS@k95FhdFpmeKQK-iw|)~~&U*!AR5*5DHPCwiP=nq|s23;g$41>(?2%l?+k z<3TdN9r`~g@E+tBQ2-{KSwx<@A(Igi$mOqN3JFf|3Ssst&0d&q>V^vl&on686QCfE68O9!DjBrwzZClap5p#GoNm? zwUm|(|1(+zi{&_yulV&iXo@9MGi1Y-NNi7n)}5#eLrx|^XQ<2l*2;l?4=(wdC;wW7 z0kHr^t|h(EG=37K1|5;*=Rd=YCcAgKKjbj;=GJ0jrl(mOCGSO3`P1;5ZRN-!X6Gf>iM)R4vXs#;eNK&p>K8STV37tgo;^voup1j2DqB3sW=gNnExFd3JWg; z7d7|6YTXfitD+)g`a{^pN#pNemP7bQ`qVgr>j**Kh9{Gvb0J}R;JEAbK?~SzLCP{W zIn7Ou5g0#>!%$u-){CIVjo4I$oC=IHEsRr99$S)C4!z2$S0Q8fqlEME>immDmi($X z&R>JQ0H-a$xgjHZ6#J89s1C;CH2!B%Th>^NU-^PCPO~sBCZTu)<>xFwR|928&cGT< zKpLN6#f~(&MgTa9Cz_lbZv@Wfi&;Ylm}v>c+`!g>n!=106CW^GT2B_89Gc313INvb z)HSC8s;#>S>qpCiL->pmP8N6k#sKy=ah*QHE`{R9Re9A|=$Bt#>TB~3OPoU;5GUr( zH@saw(u7Pmr=Pxwo!L~iL z_;l7Ay^c*a7QrIl7qjd`3&<(&D@besr2(DroPv4Z<}2elKHwgp!z z=PkHRnX=$oMJ}!2E_55VZ|Jhb51Gi;Xln>Ri}6`-Esii0*|3>EWd%g8vI>6Enw{Mf zP*0=C<30{>;{h&t!L^O8YQkPy&JkVQ-&UwUu^B+zZ|P6aO>gxP>)toN?0r*zg5*m5 zk#%@BJV7_Zak@$pHW#}1%~!$yO074j7$%JSt#HH${}Lav_cg3lUl}gA$O7fVbQ(6* zE6m?Ng)~CXYoW(_=L?|NR1}xY@t5Imj$Z4=uct{YOu6B*p5tgflb{>-;7fmEyLj#p zPr6r5f8tT`d>qe)C+LDXejf@0L+Tj3r~qDM+wt*xY2Xbi=YC6!HW01Nze3Gu#Qs)9 zbpp3~Aim_Ys)!G4u)6vW<|VQ+*)3Y$3UK=DizKc|BprwXv3 z^UkTbp5ni~$X%4g1jh8UB>1U}RlMfEEmeRFuWVj)0*w5NC>yy7ZL(uue;&V@zT-QE zf(Qv~5jn~_u|ypFHdu_XF;3o>ViiI=!kPJ!UOwv>TI=n5fvhR{KHL$+CCEfHIaJ%C z+L|HqcPiwR>3jA#9;#nD!N7bG6K7=m@WBjL9~ zPJL1Ud!&|}bE<-Eb>wfgi&F~garREVG-St-!$-i?v7edDdl0}mdT~HAgFK&bZfB_@ z`ex2`&1OO!@G`gI(iZ2+XkHn#x6JFyFcQV0V`*CyDOFLIo@_Xq4Ev+FVF6`@F9E^F zI>FsD7H`5;W*9E3#X`7IWzZDRF7o$yzNVj5bXG2w4E)jCPd}pW-vY zPGic8kR^0+KzHEB>0;wtg^dSyWPV{9w(twnuqCG#;&zLe%v9Gi`)tDMbr=TOWP?c{ z3gDROE=v%b6c=-JIxBz$RW2;qF74@)$Svu{+jzlVOi#-?EG)%s!mcav35W+#`OJDz zUVKqKKE4U0jR_Mk7O^nPa``D*Z7|maNgsIcSI8jrffu0+j%?>%j-LBh#Qnom$XYV{ z$?c_-B?~K+7Rm6Hnt8v3;LY!aDT*cUC(CB3NKBfBASLgmtzdk#9 zHbtjvgF|o*$L|%56o77UE}4mmX(WB&cX_Da-2KMl4BUY9{_h0uqRS{ZIE)`U_21F& zRk$P1vcu0l+gMi%J5)j^cn^7qDv>eeHw-c4P)6hD?gv7f+V8WR)ida}^H zqr6?5^>oy2vlBU#>**J%j4%g@^O>9MX8K$)AFbb4a~~JRXx7?LpG01E1)jF`eU0Hy zk=Zcl*%qc~oB)!aFSU_TiK3 zZLP!AxHEMK(4$lOTM{TDiy{Cnn$UQ(uwe&vSpnUI)E>Pf^#4|d9=bK>UE}6y`XeU+ zBd0y|F}|@mf)DmGMaB9hu^H~tn;f;{Tt;D5bfRIG>3e+bxGbZvE1?CzTN~;Kb|JRI zJZ(%sSbvasp7ZSI(}0N=D8c;b^&3KoJl5PzX>uP6t?aUh@x(A886dj z(F+aq^_q`uEQVXz>8IgM`+JIKgSf6EOX75k85@L*9SC@a;c;Dxoe68x8E!E?z6UTN z6a5H4YJUh*R}z^(7Ix-q6kc3Lov6nn_PNfF`6;f zaFTBouKFYKM4$ zjZ607s#e%Kif;pD`FamV3$~+^VAWP-50h0_nV7$6nKF)TBLA#=qpHG# ziYa@g)8kff?R?xWo(#MAuw@tb^Z!P31I(?9ti5J(oJ>M}lcK+Zdkf(0BY3!A;T-pu zR{l&E)P&lFFA;4~7vM**5Fo(#VJw7pC=?>1*{}ilXX6Z%@VCHP34P5wF-V}|ZAnxb zH%YhhFL031%D^39-gI4~Wo^kGyW+uQ#lfEQNZiE9v917}s(uy2jFVSKSUa@8{6`;E z=@)eR(S|}Tl8dN?y$G-WsO~E+C%+i&mZ)h*)+-UPs=%y_-5x{o?`&}y0!Bq^&MmB`TK`~Q?0{M zAQDVm@$@$cB2bSWBGjD)BksG64ee%+U})Hh6Se0T?PI}#Vk&llPjdpb9M1QLj& zrz*C&P8MNbi5z`R`}*Iq%C0iK%HEERX4K9X0#D#)A&M)Jjpfczva#cF09#$n=odn& zb^7f!I*#<2pJzcci6;B`@2Pna+HbXpiW|aZjJd!tlLnm`tS@>mxEKh+6#mfP;^;6V(;uQ8g!zQofjTCyY9Zcg|!7ULPY% zv}^xwEO1klcjx>HL8X&QzD{X>n)6$Vc$Fevn3IRACzRh&I0@DR@M0Kby6C(WEl=y# zdzgGix4Y?}Odvpt3{+HMZxlJlv|yp~K)eMPD)&(f?y_2N2h~zZkCkXaw8+AK*;wqO zb4C$&TZqr50+-bmF^~Ysz&5)9UK__=rIu4yVvR&`PiQ?I4 zwzbnseT0f&m#WUcR+-@kgZe>2HM3zQRpU~eFLDmQdB#Y)5kIGcboGNOV^IQ}l`I5H zp>-BuBG4v^Tpz~Lf;HuT2&v;aZKO!NISB9QGFX`S<%q>jmN1>D6Hsmm%3sXG{x?W6kX*6492ML^#wacj3FFQF5--A9g`sGy zy_bB)E*P=*x;DtX=YvthZFG?^%fT~zLK%PTQquPZ;g4&=DC~k~_q&L}-IgHYO1fuw z!=#)6!|uTknECh<&xn;umr+7{u}rvUJw+c?cB^RlF8qEv?K+A3Gl70a@Scmoih}f@ z5KK)(9S^u#cNL$Vb6W6m%CwUn6ben|5WcLezH_raN4b7U4+@3S#K9hdY7LhVM_rKn zqw`pbkr_UQ<;&bV&^_Zlr{wSsz>S`7&JAZ=2zL0N_!4Ih)q6-&Xnr^mW;#zzntA|Q z=tTY%EQL5)G4yIJ^7%sciWjMgU^)$$S+I`XLR7FnWZr5x!<53>nB+&4RcZZ(dSNONrJau3@?*9^T~y4wYtCJYiWp z0hxSb!rRgB7TTOtZc)``raB95IpSK7u@nHrLlD1Cl*ey;!INi{ItV^QJw*E&5I}In z4eoVn$+iqVe*RD#I5y0wDMUnLaKxFLJF~HbP{3goC>1U*2|=1BL7E8h86x&(MB`{( z5aYZM3*&-dJ@xMvdQb?CjRmL>bJ%h+6)hCkc3un0t2n5*zh)?cU}U7YOUrI zXm>wB3&G%f=*Q&h5oRf5b+*4EWnruIn5Ru%28Z(*=p9eq{0DJit$ee1Lk`B(6vCo> zv*t0CMVe<=S=hMjI(vV!O__@eqA*x)GyOJ!tC9sV_psJ=ee;>*yF2Hz#KbgXKCsjm zWi&3i&fc(S7C4(E`j;)M;a4}{V=84P|6y?)VDE>=k1d78gR%-mlod@`}Fo4=Ypg;=! z=cZqGyvb!#7Iwruba5a zV5A1X-$U>Z{A>jN(_qYF;SaYFC4-Vk1nCBV4g>_YaV_w>YTHGy#;$4nGRrzt7sfn^ zscu4V)4HaIvI03!85~DDtkacn8*v65mB>6Rh8q%L6-$nzXE`*|VTQb7^n>Nec=Rw3 zjbrv*2Qsh=c@<9(=*7pNm;}N(WJjB!@o~8SktZwTC=OSx;pcsph!JX9X1%CNL>7pb zfN*zeFC3E~@;ehdJk7XhuH2FDvjalJiJx2OBN!qdc?Kl@1J8hQ=$Du_Lz9A_@1}Zp zQ+2zoszbAaoQ7=DV?xoZIX0u_kXU~(JZr-fwpGBRybV8+meZ_M;?IG<@|pEcNf*+crTo|AHH^b4C0)2l zMLT1YceXdwd{l3$KcM^fAYJ2tiv!gRPoQ6?vg6=o5i01^{tb5!3_H|9{yC%uaTW9- z{xO2>1&-}B-%GkE`Qp2i`e+mqQ|?Y56oF1$KI3Uorf9e0gmc6h?G+P_m7mZP;`3xK z$^(J*1mOQ;?@i#VEY80F`(z^|fpe6wh#*G^8Vx}NtYE+dNH8MNAdr9y1W00l0BLd# zi?SphqJ(4Gv{h^CQf%#0T4|+E5fQg6Kmwu$p-KcaRMa~?RD&W0Mb7_w&D?hu#iu_1 z=ly@)_w)I`2PVJS_j~4=Yp$6|GJj5_!6F14?`rNeW(0;&z5dmQ7{<8MiQI1>)6lC- zjxW6@CCX^7gJPChn1I0pv$~doE4~}$CVx_hHit_IGWK!##CPxTvnsJb;w0|d`1smC zRy5WrF;)A`O^Bo` zPoIe=5oYQJBRj~>zJF4TFH(+Ysp*0>=l2R)W=NWK3x^h@OI`-G=fy!+)_J*B=CY@z z^QUsa{UV3!J(%>l=T>fQO=}KbmuYS%c9p~H{MnKITRC=Cxw^Sx^-g+41x+xck$0Ks zzuX!mw|&V~W+I$jnenmt6}clqrlY{0en?)QJ3^l9Pu%UxI|V!ElrOJMq$bsoXW@h( z2eeQQMrrv##3n^Puo;EeWN-SNkX0fcoR)T~V;o?P9OzGvsp7I$vj^4cnyREz zGmi-x^6;C`Sh}^@DyxYL9m|Q3(>2`Rb&g}|H7(Jl#~d5fu9>VgGh|U)`QsRWpDKT? z@_WPzC8jB z^xqgmbkUTuceZ&eY4dV|NC1flP^c1vIAnHv9WsTb3VS*Zb$8{gI@JTR7KS2t=)EcpL=uRj zo+E>=tY1cy`h|kZNp85#$eAdj%KuKyR}mGXw)^X$^AyL6vb{fSncI zT_lV3lN8zI+dcZmvU4I%=Y3XQSD^*7%qyr2Gvf-`B2keBM@5(|7?oj`KP{!> znv2fqI~U_UG&`ui1M{1ADc2AwBA<;t7HIFbaF{edTRbWXE$G!{e37M(NI3byPyB z7fKt#Ii@mfJe%^d&OxQP82Q{?%41};A>2&ydBXnvU(NB?6085rb~moRj|U~xfBF8! z!NG0&KOM6=#yMd%;aC1w;gjC+-c~t^2OMG&-lR9Ouh~2(p&@i{Wz4NfCq4alo&0pq zt}nb1yS{m+y1aT<+j1UO+wDd+=OdN18@VaJN4~6gunvK~|J?s}!aH`Z)HCvM(iqOT zRMz21kT9|1{WVE@o$H$H?eisMT9%==HProU61-R&8g5h;FRCirWU_IGLzJN+8kqjo=c>RX?>Jc__@ zDJw6zrwWd!Av$HUdaee_R=3>JrFFGL`ADtilB2(KqIRMO0_;+~vy zH$0J;ctc_$BhLxUay&Vot``9|XW6y)b z@a5I?0~y)n=7H>23rVkH#vv!-`F9;*PiS@Hr1H1m0Hw(V9Om|)N3h;bs~!2)m>Gwd zmyk7*S2HSR9E9I|Ze~j=O_=Xn8)0~7WSYXO^)pc(pS2+}Wqrn{_~)GFl}T@VhWNe; zS=Hb7RoL3z3%fbD)oyqfBAz=GKRxi#ErVMF?GGoJ)DQF{-x3#A-u! znE$#Bb0R7yoEsbBd|)qDrC&Ro_tl`e{#~a}E`e}vdo3x$^w&5irkPBjr0;J_SbZo- z=5N!#y0Ii-!|0TtxE0ysH{Y0y)B__9Bgs>4 z7GI=`6;eSzC_kO;Ol_EIp16#D0So3xw)q8G;R##&6n zX6Mo4L+A;<>QFh|sfJ2~9=TJI`vEQ1IRe^3mNfDyQ0vS46Mq>yB4j%Jg>L}$nR!By zFV%uQjOOgMLR9Fl)1Pb&M0dFu<7t#d*-$6y{ElW#Ly5G(4So!MkCjN!Gn>X{z-+S; zO7MN{ST?e(#bE#EW9IMD#sFzrw^xN$4B|8AEL5F|6u%y}z(yiJJvV^e=S!{!)$ImWX*_OjLDZ928Q2&Gv@vS^1 z`@mDr+U6RUY~sH?JGkX>f}8T*rG1urs?6d3``1gf)tzI%VE4sQGSO^1*F6vBK~aM>zzLx<&Bv=U4TGcB8x z(j@8>|NX+%PCUn-O!E%>`0sxRz2aIY)MuLM%1Y)$F8Gz925pmb$0N+~Vm|cE;tR0(&x`v*Ta3g$$&y`X`yS9eqV) z+Al3Tr+i-Izh696(am~hnHN%9Ts@%j)80M0#!TO{V@~fHv;L$@T7&5weW`orzJ#Ha zv~>5u(9xH=e_vmU`c_|x@_nl>&7x>>a))a$WaagH?zj{7lj((suUs+3-$NY%QAfr+ z5q>VL_l`sCZsD7RvRG4c6??rUNeJF!3In-@vqPqWQ^j0HX}E12c|M_EvgcX-~-Hswg~tN>@O z+esDqHe|ExpJYX5`tVfwI_MOyNM5)oHYq&ave~R{Vw1wBST?`bHvN*qGcB9-+Gapf z_;kx=nYM8!h0nBX)P)lMNpVTxcUm^LX+NWr!iy}M6m2s$Dcob(4AV9#N#QFjn*rJ; zH7R_RWfP@s(v!kVEt|GCR4Jw;g|D-0J`x+doX`sqXfcD08u|ykDo4X=Y*a?M$t_{{%iIZrRJNyng`WsF{AZ-2ZZ1u&k2rc1QhK)xRoBTKKv~?IL%F=CWD1oe z5mCEIr4}axDntQt)bM+H&y-CEHf8-^W|^=7j9H~}2N@S99=9%?@Yh%79b%e=A{qI* z`S?8@9gZC=d4+tGpJ0Tc^ahuq74NUS6#+R($NB6Vp>I}Hw&Ccn z8Tl3~xYH@Vwn*m#PvJG`&1J=$W~eAXCC=Pnx!ld(>%)>Oxk%$Rn)H@8L*GLXTt$%7 zw5-|xo_~MR8PC9kGaC?uacr22#dxra;Q^k2%Y&L{)ujbnuqSlA3kDe&9oqkd)Xucb_JVU) z@imsy0j{JUl2BJsE@}D?S!M1;nNgHk-fXnOXY26(N@+@!xi#Z>@$&#@V6OGocf{cw zB-9P-3|)WUic5voG5U3JpOA8}zUJ+>!J#KBRN<1oEa{t__1y&D4-D5~@gI6J>GS2i z&*$qyzlu{w<$q3^8~-}v{hFjqiYOH+JFB_O` zHBP6>^{bqCnmB-_N>J7AUv&xco*8`mJpQfTq1v3g^d6SlrKhSs=dVquPpVm!ob**m zKix|!=#~`^scxB~yQ|g1RZmVjSu)vwPWL6N*Zz_zJ?ZoI;2wfM-X*x|cu8OJQkN_U z^aruG<#sVBr^bYFhXyMrWU9m@E04Y+ zSz+O6m`P-1_8fKVk#e35dr@gRcyXc=Zl86ai>DKXk;J=mAyFJK_SHI^&xFPC8YBye z;D!k7;DtM!zYB{i;mrU(KDSLIiYi43iL*=WF6US&i-q$h$5sh9f>U6tl!gWT%?f|^zm+nXTan!Ga{EqIk$zJ zPK~^XvoaigtM$L_Yjz|wPX4wa9n3y=H|Yz)J1Sg=4R)*Al#X^LxBx#wK|H|H+;!io(ouMlI zt9gHiM$61(_)#lRvy^QS{~_VKu?{zO2u)Bv!{uRh?Uo$w!ZPWQ^QSL@Blk?5%6Sa$ zfcC%>?ZD+v;NBv#N0nh0x0lCrZ&ED;#=VgAe%TsdQs2!cFzQ=KwoPd8S@qpi0qSA% zd#0%D2?viu?>d9;A5o{*M~KUqG`Dw9X67`85H=@&Lg5f127g^;-idaS~}L(J!QNRlzqeq6c7O)D>P+7{~S zqb#%def*(4EYDlp$DT9E0d|K1Z>fao_#=ZaexO=5=~sLD_8J>ZZ+x!)UA_ps6#J$`T7 zoN4Cgq@d1!THn8{M4;w|OsjlSQsFL2wh4P$hvI$pqdE5aS(f>-ghF_cm9MK{z1shT z669@U!wrA%QuDeO*hA{B_dup`sVOKZ9DPA>b%&3=T zJIH&D`7b8KiZEqjWRI0gZ9C)^M4}?_TNTn*o$@=0;l{v=vRv+F-YPM-Z|%VA(qc%} zqeH`#JP{j1U3YrU>qT$;Sbaj)XyvC@+^uDzO4wQRzbDZKA{Mo=gY|@D`E+Wxc!wv| zx2hZBO5@A}RH59+Ja}YvLMwciPei1c8LL+&qDoxX{Y4-&bK&V=?@D+b?`(ft_t z1AWZTo|jH6d;s$;v_7|pq<3^ESlGTZ=itsgryb4OcK?BiyzqBLQV*$-l0od<980b- zBNpu;m*qN_k>oN$w%hnNyjJ-8+QXYe`0&(sDsnHY{ucP7e zoiij8=w;@%$7*)ls%pocGP%GyjcZ$SpQ5D_T`H2>gs<5DXW!^2|3N_BgL7^( zoUfEt!N^EKHUK&|ZF^F^^QXvD$*XR%vd+Fnw=rv>V~kX z&WB`&C~aLzCT}KHraeT}R&#|0Gl6ij+t^X-LoNbSp?Ny8sB*WJf)XuH-OXt5wS^I3 zW!ff*Yf~oINmr&djGrozQPNEFQzU%t_;yPLkpc| z-Wle(WJBthVWr2vCV};7%~IRP*5ib{0{oD`Bpy z=ZmBlhEO|)KaiwF_}aNnxYYi%hQK96yU=l)D(Z`!+~wpK`Ik&O%n-4BRu74l^GkQlyarsqsX+Y&(XQ=u#{<1WCFcs#zA=zXEP^uNO(-z zM(2IIaV%YZAj&(I%h0CfSEM~%nYK~g3iou^ScK{9TfLE;sb8QAPKpc?3IAstB*LT_ zTT0gYGahUyX1fG(GLC%vz5DzbFZs1ZXy}Zs8)8C34@_J?WyG+g_g8&L`#aWz1t#P9 zOTU-;f61!$4Pm`P-&mh|(XfelGXwAV)3&poo-%rvc+rDf>6Fol6ku}-=Tl|F`e`UM`$4go3sU%T9!d+I?OL!7tLwmQB#p zkC$)8>Lz{|;Z3;+M{ov+Qw0Ze zLibmcx3PC4W6r~;&^+@@(%zC1|I1=o`P^ClV(!Pa8e^lp-GUP%cSrP?#0}<^XBLnr2RZOM4y5ofn#NP<{9#F zuH;hQ1)?YW&O~{;FYIx9euWE>3N}soTglssHN}Tz%~vpkn*b^+r4LN4tdyW4agph- z$;3@&8uQR=bX?|V2BdNmZ}j-7%na3@YpJ2i6WAowF*d1o53S1#J||gh?+}-VJ|SNG zDrnN7;@99{u{)1w^2?H2=Y5m#++Jf5(lvJLt}!l8YHT5oj|dUdZ7Gqgj`Ri3@%CX_ z?Cz8}hcCx%SR1NBXhHL|)PUu|70#q_ihPorn@64w@F1jLm?x_* zG!sv(%FAD}u`F-2w(-cSaDub45tCdekG)aetGL;CVw__d%jb%T?#$g22RJh8C-zf3 ziu?YgimNJ3W}_PaNujUK2|ZllJz>r42qA1p?RMq*wBXnf-d%7&w$y&vxXI!9y?@3D zYE_GV3E}MaTrh26NLJQVGmaIx+`X}r+uzSktyq1;-1QY17YWK6ov)+?6Lza3YD(ElR+P9~ZoqRw+HsM+ zS5pKFI#WKDcNbAkk))3_U;Iz$sz}l!H(23qPErzpo>^LwFKi2+@npyw#1Td4vX3Q; zx5Ydtd*33?7aB}EZhO`Obw`+)+Wi&%jp&uh7q`Wj6nNa0B8A@siF5qhN2STpk0nRC z8_R^vI+$1FtPBiMx(Kpzv7Gb0P-5TyDy3|_ly6y%BS&yON&Ci{wT$Xv`smHpvSBpK zr#Nf*G~m0-C+9ZSHoj(dw#{4G1DPlPAoHTd$1oF^*fY&9BIE-N(>diklQmopA}eY3 ziBELxb}?NgJGyX0)Wog$u0auqfu$i6bQYJpucMNrnqqjl&H4Pz=eXIL(V2JFe$X>+ z-PsKfm#v6bIDpeBMU@^^gRG5%GbA%&5Do8^QaTnk#n(mO*GZ4z?<||F54Ph z%l--HW7{}G1Zl(>qSK@OF>3K%ntM(BH}amB%BGgc(Gd%OMK&+_#4YaXR8s_!=%b20 zVxcE*d<@2lAp+Zoe41d7h$t95l0xT?oK5FHTA=1DIy+34!Bno*G&1aE+V^DnV7M~c zGyL&z{UquCFOL9Hqx z&6oJhW^obPA(rhhg4n+dqn7}82f3ANFElM21vl_og7HQ#yX?tJI0Q{ZQxIcJ_jzY~9i znz%+J?mI8*ma zK8L5OlnzN=Q?bm-qmcBQIHzcZd5&Wa3NKUvhU81$a--Q#{4`&5jpX?$ozFky zJKqkAGgpwt(E)LvJJ*E8QJA{Qr{kpQDj$=-zHyBVafWl<;3&z+`8u!H`K^vynHv}B zESG2C0&d?pk=+g{exB&*^7+o|qpPDx7yVIGoo!7ARt*_Xr-*MO*Ns%wnrixF_(tXM z6-7C)_m47$d8UjDa>>=I)%niP_l_F@cYAF#QBJX<%y$k8-W#{$1Wf@u%&xOosWZF6 z9tp>7r>jd2K9_EIXq<>8uDWX62nesW&*eL7YY>E<9$uuHY$lm&Pv&U&7A&dl+JRMQ z#M?meh8MnX6*b?xJ%2Db`uEqg^r`u0&$43%X_Wm#{<7Q<`CPT|R)3U#Zl;_DYeor= z${jb(3(}g2Hm$YE+uW4L>(b3-|43%o@Ynj^^*5A#VDP=wd=R1|_VnikTm}wLcgP26 z!hqa0EM5ZLMB_G$A+jSdPO5VG#hwdgN>JNQ-rnjuqd?zR)OiTkIPu2KP^SaouSjP7 zU-@fmJ`M3TyL=x<`u9e+`HqEP_SbT8L(Q?!=r(`t{!e|)VbKk~e>g(-)chkX`b_j! z(O*ZeJ{x^bcLc}ku#tNZasCVM8zPB%teV-zU$es~B~vN<;U0f)w-+Vdz#zuJ6fuy^ zz=f}fD_5c=?N+-~&eV7PHA9RXDMkv#806_gF?x=Z(s{e>K+@C*^d0$Dpw|vnl$dei z)%(c~Rg9CIi}@sEh(uiWk?eUP_)BDHu-1C&PwC5!l@e9WC!x_NS;~A8%91kr^XSj~ zZ~J!-F=TrEM{>5FLUc_{bI<7A(aaNFH&J0bWM@rdEg!wdlL5sB zYvuXzf4hB~TlP^ek2=1go97C4P6xAd>Z^BL|8a4|^&cz`3Hj*ydUSQ@c3zh5@*=UBaons)KE zMR+eW;SdMS68>1*c;5-Ybf{zn$%Xee2c7HgWAwT>&e3U&8}PtPzu97@ zPyBvnI-7`^na-U}9EsyXs#aL)Fdk3H%Y|^mEta|*Vi~z7lkHVm+D_%2b4hF}qRmng zbI9XVkD{(@I#13`aF<5#BdIBM4*zuEmZyYyEN>ZhKV2%qZoC4D%#nA%>69GSB0Rd$ zh<7d#Y&Gj8+^nSCo`!7mMbTTW%tC9MZbg49mzRnQ$Im}j6z4lO#ko86f_M?flzxz9 z4ti3e62ecdnk@$-zKgr&oWeT=YD=2oELZ0hR%J47A5T z-?()$?_`tEwFl85P$nlO+XCUQRzG66UU#~b(-T6T5Pk04Zs^FDDF=00APUas)PnPkN3pg2F!p%1_T zVTN~wx8d-a^8#bGdV0tT5a0=i->ztOfa zrf1I{9ka8%@Tlk^&xrp9E#wVvd5(tT9QRR(wGj|fCVWC!chyxw!I?+-&To4RBFZy` zWkF1S+1R+XA)bh)#I{=;u0I+^>srrjz$o>OZ#vPmr|G0`+-%+0W8ui57+m%;XoL}cRx zqd^SWXK}_7j z7?>ahLYnRzpe9@-uhVUDTSLz@iz=ki~Mqb{$Q>Yk8a54Y)sSQw$1@ne8QSJ5hEuAqYfty&v3X7-*xseE*bH- z9(%u8+Z?^?)MFS<*YMKG;l#1{5u4LB+$YfVNt5~B$)krbx#?8_acIw%&hKMnxO1u< zS%zMr!FwREgY>0TJym!_T9r7ZAR4#}#4 zb%|BG`0de1GF?}y>9SItZLL%bbvuYpyTWxvpKqz!?Pz!#(RE&>if4P3+KRr@Dz#ZT z&9YXheb2K>z5ZLP)B)#RrOFCStx_*-3kE45Yt#W!Ky2;If6p>?qh6-|hqWM8@89h% zFM_T-7xgdiWElwq1MJnRUbI$O?j!!y^w0P9s9Qnl{57Y`=MsurZskijU7O+!Y5FGW zU!IVr=9V`ZaBTS_lhV;cGAL%!raFO;x~6~O z=lEksUxs=OKBf~G?|rN3WS!;19!2Y-_IRh&HJu2ANDvHv!B5N2B-uA*AR%Tfdp3Qd zUM$zSvg6Q6io?HfXlZH=e0cOw%LKv#y~x+hZ)bt`sqCC~i7KomMuoB+btXe4bI;m# zbxqYx#||ehp_K16eY<22<9N0swG9tTjW+E$bgbR3=~z>%L~-^|6C=@+-u_M1M|#{x zWMW5z*m-)Wd8ckW1vdh1YJv%z%<`S^_)qaSl0ioQee5XriVqid)6UqV`H@Gtk(*eQcLgY8ScFx-~Ys&WT*7akkXIO{Vh2Vmj7aMul$N9W=kIbEPr2oPcrgh z#lwXdtg`7kFdo}bDP?Iq)_A6|hBLL!cX&U$?3g?o;D0ytkgv{_dD~Qfw5H2u-99ro z_gMpDv(sz#><5O>LYL$_s>6@cX zbv*YOrujF%y=(Is-y)g{<09&yybx`SP@TK(NO&ueYQ29#n4)(SspAPf8d8Ui??e<- zU~p|kYFq?Hf0Xm-hUl7O(U`+3V&V=Dg&7wm#GHX^9fV(1IHnTu zRT7`~Qaj>)Dq`PP13lf$dsXb_T^)%W4IgkTpWGg)F>3MHk%2LoWfa&h3!#$wBRz&< z5qR}zc9y?1BR%58<0C?q2tBg1S)J}iIIPv5HUMF@K=*cQC_8TEWo72i z;EH6kB7-YaI6T#yIrTQP8MYEc_tCVrd?Yj}PiWI(wmdqB9ds7`8VT{(be72Q+)gWr zM?YtTj!;)nM82-qQLiVj9Wn@6el91U*e~0EuRA+4%M9nUBx#n|alX9jbD21yc!Q}V zrgEZNUbQXFG*e{6x!2A7=${z5$?fU(N;q)_uG|!fmI#c%C7Z&~G6Hf=iyfA3uSf+2 zBAwe>eIJE{o;G{3eZ$sp8VmFS2wc*KcMPG-tP4L;X`q{36Pfd&yh%LEOoHExJTo^# zt8-YfX{^2U9nm7B*;oFkvjzRkML)5ZjDcu#wlZ8w@`~clHV3nzP84*gqZ9j$e0|Pr zb5Ck}R2@}p)o;9Px6j~C5C55wr@57*Vs)F)vlpJ`Lh>G}tiF%GY0A{B_F)e4>P6zdOT6FBS1o=eyyUf0riiNL4^j~|Ax(T6;*<7LG`bwIs4{=D}d3A&Hm(?TosP7ZWVCVV_glJtz&Am6G zn$NZDtI)GVbmtC%gc&F5(@qf9Q15fYaXN-XF6SFk5u8XnRk}LJy$;pfLUCgMr>E17 z$a_fK4@QjKOXlfbCL~lT+WiPg9+C4guMkMmn8QxK7tHE|LdddQ2okf~^nZW3crh zQtOr@7@SE%^NG{=7``_~BLJLL3I?=a!Faxc;NNEa7rU49Nt zq-efI6DFE3(J+VlF8>sbLp1M8&4t2uedt5ORn}8&<6KVd1Q|Da$aV|Yck~hSmtv-G zVvZ=gJoq{7>EKQA|U(vBW5M=u~^QLD@jo!2XuU+tMr&NH#3gxahVMLWs?u z7KhjdBD>~#5{1v4X%0eQUQ;27GOrJ8pWH7oe{w&^|8$}j$z$G&RlOml#zu`vWB43SBedf9}zaAm8k?_+*aA}IA!f-I)7PL#%_(%aK8K{J64Rv;as@I8{aQ~_JW83 za$58a|2t+bWK9`Ymwp=Ld{{);UR+8mIIZ}|oAkvB`6%E=O8#0Ahn^#w%QX z=Hxp9gH_@<)ciA6I}w#csNBo`DIyQd1gerlG_mhfT7A4Mtx z8c*sj#?HOj>W`{$vuL3FbQx@+z_dtxd%$R{bm zvP&^(x)O#Ccau<6?ZjnAjW3gX47}1od$f`)-bhp~{-i}D0x7$;v>KyJ2l%1u6CR*p zi4v3Td_JJxuL`vg=>O^k0=-@pf+G;~s*n`y!nuH{ zyibyuZN^iJdRV#He=}#XTwFwbrVkG)pBV_9!*Yi~KTWD)5=VZRo@BmB+ftEMEy3k| zx2f5c<&p=z43hf-+3?gu*>hrV?l#3L(sx6or;m9h!kv8fJ+jPOm6&lDtEwoTVY^0& zyN0m{;mN$&=H$^7O0Q%Xm-qeCa2-P(K)s6+^JK~>$`rHpQ{x%qDQ_dn7C+yf?v<%z zpK;qJmwQ9GN`@G_FOxsq{foexYIeR$^>GVIT?xvx1v;P45b2Lnn2n9R_L>%1#wII5 zIlB?U*s~pLV~12L*R0`LB}mt5r`qokPbuQb-R=n+p;r~#RtK{SVj5P0!jxeW> z4Q+5fKR(v^{4B17>=#CD@51F3mLZ~YceL%%QdM~O4*pqG$=OLj%ezV1ayIBbhT=E!NbMc<&nprLVacZ~i zX4^_nm8ireETMs0$QuVY_;(IrvI-_O6i4}MWe6F_4*Y}3Y2;gV`&tr~b-9q3>YQqc0x|fny0h%eRvujNM3$Xl zi?rH1MM(y6%T{ahrZUfjKZfz)Fj>l4V^7^5DzH^ z?CN+5$5SJ5Tp^Bs>+0Bw<2&LQdk2n>baiaTafdjjh~vtxjuP^TcE>xrI+o&Cilcw> zS#-<9n@as!ywPO;_xFET4Q!n6FgBo8pbAlUqPC*smopbT)P1Nb)RU+k1rB2~>QU7F zs3KG@YC3Ky;0V;^s9014>h#?X<2dRdDiuFJLS2Qr40RzY6F+Bj97ZkfPovI~cE4Qw zp(dkdqwYaHg!&Vz8uc!!6%__os1NEY)CANmsM)9`sQXb*qPC&lL;VxghKk5{7%tQ$ zsNtvysM}DvsAAN5)RU+`p|+uFQE#I@Lbahn$YW1bf7DRa4XBx@V$>ScgQ#Dl_EIkS z)l$YcP!lNEaMS=)3@Qxu$vlT~1l54riuw=KuTl4-?m-oxvQQIHLs4#2PgEQ6oj@Hy z$?rYldIKfDFVt|SwC#Vs`m5pE^ee;F`j!lZ`Zvl0y|mA875%!yPF&`Fws&vQFM3aU zD(N>f#=JWvDz5J6($HJZ-Tc?`am$xg?s5IGVfKlK>tZK9zI^&Sf4nwWBw_lkWOhZZ z8ELo?hAjS!>JJ<#ynP7x*Evujl6qb%#u4x5_gW_*22y4$swal3o-f3Y0 zDDf7763;3S*;-@0{Gi;yGxU4_SD`vTwHVxP>MtOQaK)xz)l`7Pf(spR<JSmu~WZ5|4W`xJ06SOm&U{~+iFw}40t8;#)QV4LM0+gUlv*R>p=0p5tRDc3QB$sf){o2 zZ}&I5KiTbV_XE2>+4bK__}+w{GiTo1lDu5WBKrBcdGiaw5>M{@xrK$w&Qq|UL>uPJ z$)8)|vGvRH=6jYFTiys$RGe3mSG+85&XT1|@+7W8+i}jEyt(rhNc<(s7vy{9T%+RE z`VpPGq1L4hrT-+}aFua2C~ZwN z##qAR-M9f93dUb)7;C`Us8y)T3A75$Rk$ah5>Z#9hM;aB&{!h49Q`WPY*fy&e9zo@ zg?U#HHfPzqPI{@yD~ue^%A&lb`QmKk6fVWmUE-PRSuo#yw|8!FuC|woS#r15FO&A8 z2C(x=gJ7GZn?_d)9=p2Yo|~6H*IVdu&tJO44(6U;xU|Gutn(%9J-7J#!&~up4!*#n z>sK<>k$!vZot@j=b=J=xOU|+d#U3xE&s|VbR5*8j-r~F^9{1w8MN+=J0{6ekLT5i6 zb>r^HMyELbt85rK^OqK_{GN)+@#cEm#d#iY@eIce!e&TZ4D-_+4|?X7LKrDZ;U zgwmg-uj-1=6RnZ6bn%h}Zs{8(3#7$!Ix0i=`c4h^9k)B~>f}n;#dDX;y*n@W-&NkX z{hhZ0C2yVE;aj!TQPW-g*>(NBzH@R{E}6TS?!I8j0?z`z`g`(<-T6z4-BNbjZLTM8 z#R5;KZf*O(PkYDs-Xz-n{M*b(8NX9!|3$oAzE@8{ao*fq`?IyjANOUKxrw%YJfbJe znY(=M0*1MEyRqYLFx=A_2i&Q7CG(3H6zR{}a8Hm=ePZF6Oz#l>@1 zQeT$W^aXsH#q$eRb_#^6xG!B?BqOK$)_DsV&D~QNZ>+R(<}!@0T)fm<;-)?FN{aXd zB~WY(4@9CKM9npdj0KFIi;WWgE=RuvoNp{Oij6#U`Pj@uSFGF%jcc&e$}s+2*b-wU zvCZe1zC=TZ9D#1QWZPJz;#|VtLSv*MGsw5&Q&z&9GA0$4j4z~Jw2figO)AdIQ&yIr z8}mF_a|^v)?55dvLSKo$o7%hN#9}%pALTS^ZNbtdX)7RnkW9VBaE+X1xMnO_v}Eb> zCGNZx^YenqUK2Yp^D2ZnsO2X3@j4M%3TIQ!f$=A~; zN#_qJ@%KDR^7RHv+}}Zo`z$I@Xr_Ktiqwzi0*7k_Y89#!wGOo&^&qMW^(bm1Y7=TJ zsv6aRI*2-iI)Z9O9Y>j{6R1|yDO4NkEXuf$wHYb~)eq%HiT`+TIBGO11vLdV9hHMB zL=~Yts8ZBJCsqr`tJ>MSZUma?FRqQ;^!QMsr>ln1pQwFR{kHO;%k z$YAuC&bX8f?K{1|YfLI$U`(6qF>uSByV4MMh4?8E4gQvB9Ya!qw^&(HcETxtg!L9H zoncI#yTq83H_ynJTWpLkDh5{?lSy;3x6l~xz1x_US7h7@0e;KUWkzb=eDMzrx>CXv zD-H1yc76d!_yy8e39klG;vjd}d++v^Fu4(zCvWk*Jn6T@xm0w-YgFL)mUVObCd8Qd&<_p3w`v^|QEE&n>Z>};IU_(9d ziLVa3$;2sn zokVQ&zzob%KjZnz1;s46nrxL#Q0jO*csDqWa55g;%IGX*xrMSX15>e|59;{6DprX{ zr&CO5Nyjd)=w+U@(_Ns_&}pisROuI5{&m`3N-rtedGL_pVr#Cq^Ru+wtn<~`hn>f+ zcH=3r{4y=f0I#OLa{0T`N?%O!Zz!XoxXV9xyN&qLKGThK<9hP6n7<|blV2XSooLLU z-u~~;O|5nP7jM}Ay4s)Ll+MieU;SIh%=7JkUHz9aXW9Q8#t-|~Ifna;j(;J{RX^mP z9p3%_`wmcAcAsxu`TY;9uh`&!u<|EAt@_!|fAP@6kNon{U;X;A$DereHyfY&?eCuc z{eS#n(;xrz%;rD;<=HKN{m;K`eQw+H+h5r6;!7{T@@jQWZQagYyXzbF?0s$D>uaCJ^GI;KZYr|W_Xd&Tzd)3@J6{VyIc@RC99OD`K7H{|jw;;+0aVQAv8 ztA}4RV&tf6e{|jGG1n)tK1xoRI}c`6{@n!&7A`7Wyku$7kBdt@-et>Iti0!rJMWr3 zC+GX!?_ITe&D#In<^SKE{{OiC$4{7;nl|ai^qVH%JSF3nTaiPbHho4`_RLwg>-ztG z_x~g6-}u((C!?G_{;`_=0ssCNjDU`^_J58a__Iea!TTH?|3Vx;?4KRJa|wS~c{_&? zedKw@e~4#nhqt@L8nNiLtqZ-a6JI~Dz?Qdf`~P93?eOI?I`mrSa2y{1U5+}7iX12} zna+him<;`4822d7!$fE*;WsSB{${mS)ArM$EA?*$;kKhrp@v=pEsxrWGEwP+SR0^( zU(yJ=+^khl>rsbL!!KnGg=#)!72IQn2ew$M-*p zN9f%W;*=VZ7U>-GaGl8?kWZofC8x5!5E{Dxa~}E4#bn1Q<^Cb{CE;bYvxL<`=dhBS ze^+0^e=AX8^U04L+ODO4A6|0gIf-k@Q$3npEt??R?v=<s2V=o%P|DQ+3eT?*6kg{c@FK7ol)Y&al)dCuP<3;7y1~o9Q_M+&!FbGZ;BasVI2x3_(-iOuFdY^-lf;WI0!DMg~m;!DA$AjC!iC{G-jobiE0vo{_!9(Cp zU^6%wG{Kv}R#4{mHt<%^7|L7?M1onM3zT`jA9y?H2Ib3$2iJn5rovmK0}RI;0*(bk z!E`VT6q+I&oQ^pH%mKTBg`mj#tpK}&!t09$*I|wUtH2)M<6uv46X*oDg3{rt!3#j4 zUoHfNe(447!#)-~1ojqpun*V@_5;s?7lDz9^an5&ycl$Y1Hhr+KyWm83785F0y9B3 zcqe!%SO8uIdceV8DHsPn2o3=s1uqAm2Co3OfLDSqfmeYIU;=m$911priC`OeH5fL` z;gWmmT;M2h0C+7J5B>-o1zrcHfTO`F;23Zwcs-a4CV@rZ4d5y;8C(yhfDeJ=!HwWV za5I<+?gZ1oMsN~%1iTS6!JEKS;AGIan*IaEfK$MJ;4NSrcq=#@%ml}Rw}I(k7C0Tu z26MpM!6GmZECm;VRbU~w5iA2YgAN9e?O+%vv}!kSC+6;8BNzo90b@WDlmX`y*b6j< z(;vVXun*V|><`9)1HrN2P%s@#0&~C{z(Q~$xB^@RR)G!%s*PY6xEbsQZU?)AJHaTh z5sU$kfc-%eOahH-hzE=Tg{mC@hJo>5H*gf#9ZUhEz$su1I1}s-=7LFJ5$IsRD+R;A z2f=ROqhNRNX)p@h0?HNmFM<8R1~3V11|1BHtzdWXEEokwj=(<{3-$-yU=lbQbTCk+ zgWbXDU=)}G_6G~WByb(*aMQ2A?%+l+3fwI2;C6A3BOl@p9u#-5S=_-B;vP?a#2t(r zi8~kzI);)Tusb+Z?7`7uKb-GV?7>X22k#X7QGB0b4|>ENEEW6F)R&mSN5wpr`Vup^ zMa(JGmzco@!Bp}om`*+gr;tyDCz->1vTdyxj@C4HlU8?8uGK(QRx=f zbw2aI{AAstQASkZ%@$fH9C0ygf3n7qUp|zD{0c1EM04{ET(v);)8r>>IQhw%O@2$P zc$Zk_BFkK4`4?(ienPj%Pv~v=l~{9siSc90ESz=f6dxM3KcNxjw}2AKPxiazSJGY{ zov%X0nV0=MZO*syxy&kG0raK(=2Bzw%d_&c*viLTYCwLAtZ+h=%dfx+FMG7|TV(lP zWW`qk)hj=*6|cuizsQQ`ZYv$38s#SxuKX5JbMjlBBJ8WRnfTctlFvU4Hxve1&EPeZS%${W?% zTlzvGyBIQ;OMejkZRn*>Bw|Y!MejwQ4wql|4Y8l5^g@l_#J-U36N#+s#s3m=CwiIx zrEes%`WJiYAChN@U;0QQI`Jd@MD!BB^c6`<{78S1@=E@t&m>ZEiKm3x5PK<$^qoX< zz$b-X!cJFVr4LEE5?1<=Rvk(iC#Zd4>01(~)Pt-wr9LHo>0^ncDzuCA zGto)B{}x`1 z^cm^nqL+Rp{ao}Czw~vfw{Mq4`o7(E^HlzIy7Dd9d64hH*2_21q1Sy(@+sejZNFHx zg`IzCN3AdE6jr_&sW%BbU*-8mTGHUFkapDHg8q)|&{7`xo}`s$;nV)EBo=91gC3?! zIYHH;^key!biFTDb$W}cBmEuTWK30YNEs%o{-^iCbUtLPm6&JZM}JonR6WX=YWtUQ zRQjLP?-ErW`rK+@3duG5&PzIk0foe$wrNjr|`Z`U@`>FE}*)5}nK(e-wdDy@z;Q`zfyGgJ%c zdOLr-@)hacV#hmOwXKd<_gNipswz|GxVpB%V)a#Z&Eu`~Tp0(Y?2|3Mlrc-?w}eqe zOuFUl^k-RR(QP8%qFrAy#!DD+=^93Vi8`GLsvYdQkZ(fwzsV~9+Rt>W&q?azRXb=u zQ`EO_mtEGEIv(AVwV#_6e_8v9r2N<4bh z(CJQ7BZ}6iTkRr#Zn5SU(PygschGV&t4KW+E0jO77M6OHHT+O8CCT9u%E=9n0WhXu zJ_ODL-vx8Qzk)^JMsOAQQ*b@_CioEecW@*47PuMwBe)%W0o)1h1RKF4;1N(p0u$T^ zo&sM5jj{08fdcY%B^ZPGF)$8X1jb{3DL5AMd@++>2bhjo=HThzYA^?U9vqH)2)F{X zw0k<{FmN4anTM*t2f$41!@5BwW=2z&+<9)rxS z$1(fC9Kv@4Phl1wMgitX&=|)Zr9ce$I@k}~490T;Aq-$Fj$H?4=lng zyr%~-7lN6XF9#pRTmY`1JW=4&n5TeMxDNxjU@iemagPRH!u$}}0RA342>u0Z24&ni z0d51^z?VSb840f{ESY^CP;{4AIP z3awLxeGhO7=3BuW!o`C#F)sw&m&bm@C0X;{6f0 z5%U_b0ryyNGv+LD$J`s-jyW6L2|gOvikOxRcMz!0DKC!5r`vun4RJOTjH*6}TPT z2)+hx0qemw;_DAqW4;rVwfzU+KFqb?aqwmEEchSdKEdJo6=;y|#lTR^zXnHxcY)in z9|@*nz6TT@Y6_T%c>~yt`)Du+b17(I9s?F)E(0Sm4**wSo(--8mw+zp2ZB|Y=YS2E zuLU2+yb2UvwHw@oc^=pg_e;R7m~+5t%%cP`KM5WJ?*(UKe+Afzc>#D9Tna|=>`5rt z5Bvuh4_1Mrz^A|z@IS#R;Ge*m;3O~?ECTgfNOm6W`Fk$&zOA3f3d+{!s+GE)%kvaB zMe7$RPK4GkRI7ijU!+#}T3^U&){aL@>}h+Un{4}~P&c;z$DQnD#bVpb>ciISm7tDi zrIlw{UujByekU4U+vvZAxZ7(;;fW-YGl^640yLLMufb%0QtXA!kliQQ@#_ey`2|9q z$l6K#%vbdz=_XP;QeMqF$Yot7dPz&d+j=eAsO{yiZ7(~)9eOo3U?VH&#r&0@<~`i4 zs2oj;&1KbP)0*>CKD52&cG-H#lh*6}Y5f9~w$?9HZJ_l_I>l3@`hm9BoEoi{T{AmQ z-G{XOJ*w?B?ItZGesn!bpVj*L)Mtmjvwequ*&Vh0XxUL6R{q-dcdI(p_64fVw0<$` zf7{OzRj1lsbC_(s)VrIRy{^;Md;;BfHUCM=&1!yzZ7*v( z>C?h%5xwRvY95v5U}_%BT-6^m??-bIg|C@NZAuySI$ZZ-t=HU6Ew8c=UX}19bS^D1B$<^gJMvE~74ja{LCsC-TxM;f|8_2e z=94Z{{Z;cfb-H>DEZ?GhnfCsMn574In5CaF~%)ebY=$cOljgw&V)A4ou z)wcHERvo`#qit{F@QGSyV{1^GTh}_2rUTEs@yp$@71ySWE4wnc`!6D^XRNz+VdjdiOQM(dVr|dS&TyV{XX;X)a^#{H3Zm=$No;C{nThQ0gi2D_Yf0;`?jrx$P8~&7h|AEGa?6)}Oo>G@Ma>SpL2czI#;B$sZB^57W0j{9uShID)E6FVxm7hC5Lm~Ra~H?=mo zdUHhsc+m+mF?hT0- zf0~TBs?TpLcPBUg>XEZUANto=Rs@AJ-Sx?@PfwY*$<56JnFE3kZ>>+>HS%W#LmiZF z;~V+EO>Rj3&EN;_ui4H0iLF^NJ@+*vzrXR#r?!4TeKmgk>HD+xB!Ac(yeGuX<1y85 z-8iUaPqI7iv%;Pu=^rVf{->AhOZA3q zB^Pg8aoOM?^_4Pg#|IJnl2^Sv%__2b|d}6gGzt(W^%zJpMANyi3N|K}K5_DN&ui)LD{5c4I%R*df5&AL++)d4)q;zDdC&gjU)`B_>B`UO|1&oa zzvsFA$!pdH=iTro^|Af>-iKTFC%@k_X7I3?SBx{#C*0ce^0$)D40<}=wWc@mUGN|C z=Dn4C;YBgS`fs^#oN;Jk^^YHYD|zL0Pi|f?hMOEpqnFkE{jKCzBb~3j_s{F;4=?`V zZ!rgw|DX1*20p6l%D;SoL4u}8Em*9B29s6@@B8~>fCLj8U?2ga#*&bkkjNw%l7S|a zl|fPI7H!ms5fCC9Xeww?qsEFF6?c(FH_BJUpi!e@#hNNws3`lt@68L7ROohh+u!cC zFTb1f&bc4&+>dkax%a&}m+g9d%)NI)p1(=I#^`5TKg^2i>nQ9ya>&+iF6@aoxC`4a zac~zlJ@dMq-i49t9NdMz1P6Da=a*e}dKdQ0ac~!=pE_vAyRhh24(`In`3~;F&hs7I zg^?Ey*!f-9vdqC<*vUA!3qwcu+v#1H@(TxdVS2#9UFf~c!ClzdyU)(=!lJDX?!vM~ z4(`I^6CB)ywV&^`^SdzeYX^5Bb(4d;@c0Y|cVX>!qWPmR^zLg;eWI}E5eIi+*9{Kt z!t5Ci?!vZ<9o&UY7@uUExN!9g4(`IxS_gMwLAiswu+4IC7dDM_a2HY^cG~5;uN6YGq!X$4}Vp0+bR3g5kBO!YASu5h*T&CCQt52dFyANt? zcM>;x9oQF*cd$_aWYEb|y?{di$)Sa0Pcs|%NWfe`51q8k7&1>6958DJA2v7ws*#{;$iUJkee5GFL)MxkIr+b~@0;KjHh zbOKpGVrmmJn~+Na_D{yC(qNVCn5ndQfo*?RRQXFuo}l09>iH#3#Qd_lP!%lmQY0Yp zrPa~+I+~ha#~@Tv^I@oy{kQp*f!c6!DZCPqlxMsm(U>UgTQRVYA+z??V>$Fw(fCs< z8uIZT>|-irK30e|_-=vhHjppp%kZvrhW+;6$vFvcktf^VGCALcoW`9s2}w1{UZIe0 z17_e2ZB#RO$j(JIehclfcMv>SX_Ng0vR~u@?_|H!?zTZ`$vzp`aj8PQ2Q{n$?#JEf z+$jF6c}nn9^7|FCruQI_-8}O9fWIss;m-XX%;4w6{`$eG9pN?hxn!`^U^k!ye`NQL z46=5@z<>1$;hi)3pU75cob!IWzo@wWyZ_cr(wsr%MoZfl^SrrJJ|$a6w%?Afc*xWEQQK-LLE_q@q@Ci{GiFf0O+>m=OLTnyFYeTxim~v1BDhRwb zrYw@iZPM7XV)K!*jv-x3Oj#sP8}Rlhjl`35 zIuSG)vW&0|;ZX$gP3}VMtdt|HLAV{E>oSC4wx3SwhuGUV2WehMr7loWP6Bj0(wsaW zx;YQ;)7%7DA9aB4**$adZK13I)?+{IxG1BZK% zaB-?Y(qK&6K5sH;x}X7&R7uj5>+2ZMNm{mX~_0}NQ$MJ;;15kjOv=|l2jTJiFd-F zMdJEtHt&QUdl9eb1n-PO7au#`u>@UwaQuhSc#^jp_inm zxB}-eBb+JzJ@~o(3?VjtFds8!JMr;=$q4Rqnq!_r^3U}->F%CMrEEg*5@Pf^i8v&; z+rfoy9GZ_Jp&m}bFCB8%7@}jFa8GDE00H_hLUW%V%jccbsFbw`B%vE0+YVE%q*A6K zkoX7T1C2l)uEfc+ynuxWghq5Zfi90kFHDK1@A`(gpCFF#O!A!RB6ct-Z-U^YC3V;k zecs?aB(2MT2l9}3g7;p7_Cs)S0%Pk(@Do=%ZA_jM`#e&|#A^q(BS}v#!b@ygg3D(~ zOY*q<#g0Ql&s=b3{>BBUl<^3$`3LjArQ+=I&sLs29v@wtwfw-K_`&>N44-eY{0)x3 z?B;>-gZWR642&O~|0?*5J6rk6U1yK~&7A||+hWECDR=nVf$@W%zkg8qgZZz!XJG!; zLFJEc92h^i{K72*<74HM@b|OUf$@X$FM51neC+rq`J0{{7(clD;PYpX_q{kUe(>`j ze0gAeZ2OdB9km>R@DY0v*&+CL%wa@!h^&#&2)qfQ1;GpLKo-qAasM0wk-Ey7Ja|d1 zDf7*^GQ$rq#x-Sl$x{=q3L!Qd^PGFN7L&OOzSxUS(|jeEhhTS*`Ov*~fkw*hi%It> zXuFW^o319r{liy&-2WS2`J;x;gM-#-Rn>WL^BW8}&Qht@si|=A8V+PFg=b=X^4B&b zFBpKwyxbvh9BLCO>ON{J+$smMi!1#>DCjsRN51VD7HiU>K{!Y*3DqI@Qun@c9_2}# z4)4t6Q|iL_;rhICe_(1^aW!>q!j-|Q1;s(fo#}!o<~TCdhPuh}tNQa&Kg^mvBP*9< zZ6TokNM%*}eZ}RKSbUGfIS32OE2CI;9DWEt6}|$is(s`Q=!k^baHtJmvzb$=s}fKf zR6Ve;x(Y4_=apCbAxuxkBVl;flJf9$c>aV>Vd~oWAC?D$`Q;VZaCtjnP6*~`BP4F0QNzBEro?eH1?%eAHl<#`bE9cDRf10;h)M@I#wZSz0xZiVzN*f~n`I zOt^zC4wsZgi$IQ9@i{e7wy0kwN-#d4{Jo_7ygEFx0zdSJe{s}zS+pme zd|9>gs3CD#l}jqfvx3ESQEqCe9FiAp@+7wQEFz+l&Di$?CYWBg78r5=Dad9C;G_i;Jfv-H(RW3fjeU}2OhzTD@y|KMjQ-8 zQ~!3zoXRqL2>Y{Km$q=wVDGK9kkNj|XpL}>kyQ;5aoxo39^yJ%2)HEbv4otOC{k5D z4N?V9$9A+Vho;# z@rN=7xf!OYj}vkO#Y+Q!LeGefn86XZ#nf$3+~i7sUZ}j%ZcFOWko$=&20>^c6B z-y|5AbFeoEW~-c6;t0qsUjWzAbkp+xL!MyeDoN zn%maF)P45-v~u`~BRTET9!@kDe=1>==(YVsY9~+iQPi{K$uqO2 zllxvmo$4clfI4B{S5;tWqP~@Z$Ae6kYKRSU?H3YXM2j0o4I9sM{zbnLBIZ9Y=w|yR*wnMAh_p<{|udE^?Jf@`Q z!8JsKlr?2ZX=$JuV`k>idGM=k`+lJ=BTt%&^9i7aW-bYbmY}&Q>XH~L#12onnv_dQ zi4H5`0OtLuE_C{IhEr;^ta(%#WF>YSev<|F58B`snDM^%sll|r1b=wUvp6>0vFAxX za!)STf+qK|<#`6J`-$Bjdy%~ZvN!1F>-_fVePXvvl0NpH;O{3qC+;lE%!n=er@M3R_y2QT<1RH0#oX2_2c>Pt_`I{GdW{02-D`$3C z`8Q|vulm3<3<8jy$DVN*c>bzv{jCfCKRz7d;)zEfyz5E8KKYGxH;#+8-`2OBoBOvo zaOI|+ljP2OL&SzHNdMfw9QqfJ6a6RuSAOk~yixere=_~#X!^&I{@*wEZoY_mZ}XAO z*Pglj8}7Gr%o>6f+@T*G@=)^+U4Q6LxA<4??RvB41Y8K+;Bt1l$T-jG`@iHa9{J8R zFPyC$l5g-lbf)y~KJIGF2jLt4d{_~^)*yW?(w`~dwIWX7_`iSsIq$FWgRX?=<<{J7 zUl(7}=-xRHuSV@NI>^zsgpy%Rp1YvfKfnLn6)(;>^LnY+__M{4W4Y#+!`?@pe_a1) zfxqt-z}c&40EEcEcEGu(Akg1HCw>mI5l7zX{?j)oU)`#$2;yL}>KeG$02$(VX~}`mUii% zDUG|-&WD^efs!Ryz1Br>P^trqaVSeT;P+LRFU1OVVW6fzGB!G^7Fmhs(cHk&K+qE; z|BbZbnw-j|Rf_`EX`UtJlS{}6OGa90aj+(k=9$!Ajy~zkPthk0;7OmpE7HiEzCCN zY33DXKa9}igWOYG zFW1L?!42Vu^W%AepU$u3oA@VqO7IAm3loGQ;U?iWVZHF8ut(T091?nk-wGqe?~3EZ zG*J<+5@(4E#hb<3#kJxFakJPWJ|sRSz98-qUl-pNKM+3_6Qv8Ki4rTx(n9HOX`{4N z+AfWf$I3Z!gS<_CTs|USrCg)TR{}~%S*ApkpDFc9ld@IWt~{dbQQlB`m2au(YKAJP zx;j;zuFg}7)kW$Ob*1{Kx>tQ)J*rODuFYe&g zJ3ttKM2?ZL%J;TCH8y9())6hV{1fvGu8S z!a{d?@O={cE|DHakEVHArM>i(^i29XIzU&VH}9m^(GSo&=*Q5fFVg$zH|QSvDE%pY z5yLW*nOtTg^Dr}#y_mg}oxn=0kDbdFvJ2SN?CtDYb_06zG4?6;dA5UnjXlKa{35=V zzn9;LKHJH^#19uP5T*$};YY%9p+WdcIA45KJWooOu9Hfo#Zs;GGwEJwle9(JCOs@Y zAw46#D0N7Cr9;x&(h;doIxbOivixoNBH1JVK<4F4IbZh674mKJ26>D8h`d{lSB5Ji zmC?%glpiRxvPH>J%hh%2%j#!pyf#L=L>sSNp)s1I8QN_0%3|%u+E2B+v@f+JeS&W3 z!;MrU!^k%BjjN5I5ixEy8jXjISB$TWbIc3O?dF$eo^`dgz*>YBUS>tCyRD7ZcC_(t ztfe@7pqRKk>j+$XU(NXl%*uT(3yD-S6U^Gdfp)nr>7&dvGt0~~!JHT09RbAA zGHubBbT*w!`{=FoM|2N5Q_s}{dWF7JU!m9Q_v_E;AM27a z&6sIiXFP2@Z=8#s#Pf@szM^OaqraB93mWQC=3kjtnQrDwCd|ITzQT^+Xl@m^mRrYd zMtl5*OGI0w@)LN8H~4I{MfChNaxWo)Yx=)x5q>V!O zb_HM_J)d4oH_*R;mfZyn+ed$g`5u$a%w?*WbxaF$m^s26W4_0ZXXmixYz^e^*X%C# zefAG*GIt?oa948Ib75{hw}pF#JIHl&?{O3OBEF8_%0I}r@&C%d%pMjp}p!d=r zWBd$dE?_QZCNe9T2IfiTZDt}rldtC=fb@UG=LoZfxk4edcByc^5EgC~?iRKQZwN!g z$>K}m7h)=Oz@73v@_iWN0i{sgs=ldytuk5%`t}{|1MLg#96ee8mOf5rF{*XFSg+H6 zh4y<%e^aN7bB*sCoM9PrjK#)H#vR65W4+O4bQ{07<@ZAKQZvI`fHC-h`I^~f_L^Tn zo+)b_bf<-SEwIY18?8I6X6umk4t~dm@7Ynm^XPQw#Rf>o0r~`e9cHwRn8k*&GFtj3 zb|Yr2ui5W#v$+y(DYt^VkK4*^=Z5nmF;YDIME(Z;CVn-48^4x+RM;u(!}y@ZJH7_p?ZZnMGdGC^)_|A`T$1x9<;?c zElr!NEjO+*H(@q-os8oqTa>7OVt2EfxnVrVmq5$L3u6RDcuII*I4+zkjukURuQ*%u zi%Z4T;yQ7=_>B0L_1k+eL%vJ? zSbhX`QdK`>cD;JBHdCw9ex}{6J)w1JS^6UVPW@3m13LF7#yVpz`lktV+<0r6MLx>j z4qm=P8}w7O0E?o5d71eg^Es1@xn~l~vs2jp?1$_p9Lrn$_52=wu~;Xrg!J7Z{t8-T zj5J^85m@~vyE66+T0c58#xV&V6!7%lHZ`%|5;hrsIy z=+shrE#&lNjF`9R3tQSF}nCY;C!fZ48Z!g;e z`{x8(#4X^%{4M-2;alj(al-e7D}|ZD!@|?TtHNR7Lt&Wc6PJlMiknc&r(nw*md=xX z@~v{S+$O&urzrO--O73DQuPb<7HzBcxHeo*HpUtg4Z+AV<{GDrZPsJfOV(cNBWKpK z1sSRa?L3a2N>8VY=`#90dM&euABs7;QP?0f2?^pH@h@oTWcP)9v)sw_LX#b3jx#42ip^xR*<4r`1#A&p#)jBhHUb-?9u{L0+rsW(+t_xr zStr|tRy)G>vBz18OX5bXX)iEH6@aBW;W zx0~zay0{+h2yD#b9K|Q`Dg0m>(QP~LW{6NXcO9@ zYdeJ`XtWgF1Ir`b@EV!0KGIFvluh0Anwe&{nQQvYe6!FjGW}+m8N@sqHfzmQ<{GoX zY&17OTAGO-z!#iH37|*8rW;FpV1K036KERtos3rY_RsbCbOBvR7twz7a*z(uVOS>1 zVVkU?SHt$Jhpua+H_%OVGu=XOhZWdLw_!BzhJNp)_t9N+H>{Ul*e^%>Ee487WRjR< zCWRTrjAq6%9wwDZXC^Q-!!t6YGhQYWvsEtRWAd4T0rId<@6x;V9(^>%r_%xx4BFrg z+0dhHk!$#%848R-qX@EC2CFQD--0{eCMR;q+$e4=mxcuwasxm+%vE9CrK5cAz~ z%-w6a2G}yq(8;abPHq?1!R^ER*UKH{jzOa(^2z)teylyG^Sll#CzsF1tnTN7kUyg7 z*6^rBTvYDOH*v@sbXmn=9o*@A{>n z6qc4ttE4qjgR}v5)OM*=+9~aVExk|bmU^Y5(lP0zln7gztaVc539zMgSkt+%F$-Z) z2j#H5TwW!w!76A2^vZU*Ro*G@k~`#ma<|+oAC-^EC*?#XSsA5_RZ^7+3a{u&rjo1V zD}}H|gGyLguB=klC=JR6r5STeD{RwUN{6x!x~Ug>_n2~0NmP^7QR-NgqMiQy+sa*+ z&?6iX`Y=OMVv?95jut&)I_75?b7eL(Y=KxLmWd&;R*YcQtjDa~g!SVNu}y3jcZ;23 zm)Ij75&OjBA|)kBDbi@kgLMpzm84h7mV8oyR3w#2A*^X4(rT$*YLuF!7HNmnCbdhu zrB10!>giw8B*DHPjWtcWOv|$Dm9wGG3*;i$`yshjj>xO!dbv?r-)?(GV8mrbutXo@@9ZH+huIyGi zl`hyCN0dIy%#@m>rog`TU?oedvg%c{Ri9d*7O7=wNUenpvRbWI8`UPY1@?BE+76B1 zsdlM7>Jhb1J+4w(k_HF)nnz35Xie5AFZ4Nd(K>UZx!K%m>R6fPV&*Kg{8kVvr{&fv zYmL=_8MN76J?+FAwZqzHbz8mGQR^64Nx`UzqdEW*>11fMu{6;w$+mV6L5n9tPkU^+ z&4k1jKtBg<4P6i2+zeSIy19$rfmZ6qNIfo(LhJBo8PXGT>?0O-+*^pl!B)9-FTm~5qL5gc3#}Pk;w{*tl) literal 0 HcmV?d00001 diff --git a/docs/QUICK_REFERENCE.md b/docs/QUICK_REFERENCE.md new file mode 100644 index 0000000..35ccd51 --- /dev/null +++ b/docs/QUICK_REFERENCE.md @@ -0,0 +1,157 @@ +# nsSplashPNG Quick Reference + +## Basic Syntax + +```nsis +nsSplashPNG::show [/NOUNLOAD] [/FADEIN [step]] [/FADEOUT [step]] [/NOCANCEL] +nsSplashPNG::stop [/FADEOUT [step]] +``` + +## Critical Requirements + +⚠️ **ALWAYS use `/NOUNLOAD` when calling `stop()` manually** + +Without `/NOUNLOAD`, the plugin unloads after `show()` and loses all state, causing `stop()` to fail. + +## Common Use Cases + +### Auto-Close Only (No /NOUNLOAD needed) +```nsis +; Splash closes automatically after 3 seconds +nssplashpng::show 3000 "$TEMP\splash.png" +``` + +### Manual Close (Requires /NOUNLOAD) +```nsis +; Show splash, no auto-close +nssplashpng::show /NOUNLOAD 0 "$TEMP\splash.png" + +; ... do work ... + +; Close manually +nssplashpng::stop +``` + +### With Fade Effects (Auto-close) +```nsis +; Fade in, wait 3 seconds, fade out +nssplashpng::show 3000 /FADEIN /FADEOUT "$TEMP\splash.png" +``` + +### With Fade Effects (Manual close - Requires /NOUNLOAD) +```nsis +; Fade in, no auto-close +nssplashpng::show /NOUNLOAD 0 /FADEIN "$TEMP\splash.png" + +; ... do work ... + +; Fade out when closing +nssplashpng::stop /FADEOUT +``` + +### Prevent User Closing +```nsis +; User cannot click to close, must wait for timer or stop() +nssplashpng::show /NOUNLOAD 0 /NOCANCEL "$TEMP\splash.png" +nssplashpng::stop +``` + +### Custom Fade Speed +```nsis +; Fast fade (step 51, ~150ms) +nssplashpng::show /NOUNLOAD 0 /FADEIN 51 "$TEMP\splash.png" +nssplashpng::stop /FADEOUT 51 + +; Slow fade (step 5, ~1530ms) +nssplashpng::show /NOUNLOAD 0 /FADEIN 5 "$TEMP\splash.png" +nssplashpng::stop /FADEOUT 5 + +; Instant (step 255, ~30ms) +nssplashpng::show /NOUNLOAD 0 /FADEIN 255 "$TEMP\splash.png" +nssplashpng::stop /FADEOUT 255 +``` + +## Fade Speed Reference + +| Step Value | Duration | Description | +|------------|----------|-------------| +| 1 | ~7650ms | Very slow | +| 5 | ~1530ms | Slow | +| 15 | ~510ms | **Default**, smooth | +| 51 | ~150ms | Fast | +| 85 | ~90ms | Very fast | +| 255 | ~30ms | Instant | + +**Formula**: Duration = (255 / step) × 30ms + +## Parameter Order + +The order matters! Parameters must appear in this sequence: + +```nsis +nssplashpng::show [flags] [more_flags] +``` + +**Correct**: +```nsis +nssplashpng::show /NOUNLOAD 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +``` + +**Wrong** (image_path must be last): +```nsis +nssplashpng::show /FADEIN "$TEMP\splash.png" 5000 /FADEOUT +``` + +## Supported Image Formats + +- **PNG** - Full alpha transparency ✅ +- **JPEG/JPG** - Opaque (no transparency) +- **BMP** - Alpha if 32-bit, otherwise opaque +- **GIF** - Basic transparency support +- **TIFF** - Alpha if supported by variant +- **ICO** - Icon format with alpha +- **WMP** - Windows Media Photo + +## Troubleshooting + +### Problem: stop() doesn't close the splash +**Solution**: Add `/NOUNLOAD` to the `show()` call + +### Problem: Splash closes too quickly/slowly +**Solution**: Adjust the step parameter: +- Faster: Increase step value (e.g., 51, 85, 255) +- Slower: Decrease step value (e.g., 5, 3, 1) + +### Problem: User can close splash during installation +**Solution**: Add `/NOCANCEL` flag + +### Problem: Script waits at show() forever +**Solution**: Either: +- Use a timeout value > 0 for auto-close, OR +- Use timeout = 0 and call `stop()` manually (requires `/NOUNLOAD`) + +## Non-Blocking Behavior + +The `show()` function returns control to your script **after the fade-in completes**, allowing you to: +1. Show the splash +2. Continue executing your script +3. Close the splash when ready + +```nsis +nssplashpng::show /NOUNLOAD 0 /FADEIN "$TEMP\splash.png" +; Control returns here after fade-in +DetailPrint "Installing..." +; Splash is still visible while we work +Sleep 2000 +nssplashpng::stop /FADEOUT +; Control returns here after fade-out +``` + +## Examples + +See the `examples/` directory for complete working examples: +- `example_basic.nsi` - Simple usage patterns +- `example_advanced.nsi` - Real installer with progress +- `example_formats.nsi` - Multi-format demonstrations +- `example_fade_speed.nsi` - All fade speed options +- `example_nonblocking.nsi` - Non-blocking execution pattern diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7347e2d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,306 @@ +# nsSplashPNG - PNG Splash Screen Plugin for NSIS + +A modern NSIS plugin that displays splash screens with full alpha transparency support using WIC (Windows Imaging Component). + +## Features + +- **Multiple Image Formats**: PNG, JPEG, BMP, GIF, TIFF, ICO via WIC decoder +- **Full Alpha Transparency**: Proper PNG/GIF alpha channel support via WIC and layered windows +- **Configurable Fade Speed**: Customizable fade in/out speed (1-255 steps, 30ms-7650ms duration) +- **Non-Blocking Execution**: Script continues after fade-in while splash stays visible +- **Auto-Close Timer**: Configurable automatic close after specified milliseconds +- **Click-to-Close**: Optional user interaction to close splash +- **No Cancel Option**: Prevent users from closing splash during critical operations +- **Manual Control**: Programmatic show/stop with fade effects +- **Zero Warnings**: Clean compilation with Visual Studio 2022 + +## Installation + +1. Copy `nssplashpng.dll` to your NSIS plugins directory: + - `x86-unicode`: For 32-bit NSIS Unicode builds + - `x64-unicode`: For 64-bit NSIS Unicode builds + - `x86-ansi`: For 32-bit NSIS ANSI builds + +2. Include the plugin directory in your script: + ```nsis + !addplugindir "path\to\plugins\x86-unicode" + ``` + +## Building from Source + +### Prerequisites +- Visual Studio 2022 or later with C++ build tools +- Python 3.x + +### Build Steps +```bash +cd nsSplashPNG +python build_plugin.py # Builds all configurations +python build_plugin.py --configs x86-unicode # Build specific config +``` + +The script will: +- Build for x86-unicode, x64-unicode, and x86-ansi configurations +- Copy DLLs to `../plugins/{x86-unicode|amd64-unicode|x86-ansi}/` directories +- Clean up intermediate build artifacts from `src/Build/` +- Supports parallel builds with automatic CPU optimization + +## Usage + +### Important: Plugin Memory Management + +**CRITICAL**: Always use `/NOUNLOAD` flag on the first `show` call to keep the plugin loaded in memory. Without this, NSIS unloads the plugin after each call, losing all state (window handles, threads, etc.), causing `stop` to fail. + +```nsis +; Correct - keeps plugin loaded +nssplashpng::show /NOUNLOAD 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +nssplashpng::stop /FADEOUT + +; Wrong - plugin unloaded after show, stop will fail +nssplashpng::show 5000 /FADEIN /FADEOUT "$TEMP\splash.png" +nssplashpng::stop /FADEOUT +``` + +### Syntax + +```nsis +nsSplashPNG::show [/NOUNLOAD] [/FADEIN [step]] [/FADEOUT [step]] [/NOCANCEL] +nsSplashPNG::stop [/FADEOUT [step]] +``` + +**Note**: The timeout must be specified immediately after `/NOUNLOAD` (if present), followed by optional flags, with the image path as the last parameter. + +### Parameters + +#### show function +- `/NOUNLOAD` - **Required for stop() to work**: Keeps plugin in memory between calls +- `/FADEIN [step]` - Enable fade in effect. Optional step (1-255) controls speed (default: 15) + - Higher step = faster fade (255=instant, 51=fast, 15=normal, 5=slow) + - Duration formula: `(255 / step) × 30ms` +- `/FADEOUT [step]` - Enable fade out effect when auto-closing via timer (optional step) +- `/NOCANCEL` - Disable click-to-close (splash can only be closed programmatically or by timer) +- `` - Auto-close timer in milliseconds (0 = manual close only) +- `` - Path to image file (PNG, JPEG, BMP, GIF, TIFF, ICO) + +#### stop function +- `/FADEOUT [step]` - Enable fade out effect when closing. Optional step (1-255) controls speed (default: 15) + +### Examples + +#### Basic Usage +```nsis +; Show splash for 3 seconds (PNG with alpha) - no /NOUNLOAD needed for auto-close only +nsSplashPNG::show 3000 "$EXEDIR\splash.png" + +; Show JPEG splash (opaque, no transparency) +nsSplashPNG::show 3000 "$EXEDIR\splash.jpg" +``` + +#### Fade Effects +```nsis +; Default fade speed (step 15, ~510ms) +nsSplashPNG::show 3000 /FADEIN /FADEOUT "$EXEDIR\splash.png" + +; Fast fade (step 51, ~150ms) +nsSplashPNG::show 3000 /FADEIN 51 /FADEOUT 51 "$EXEDIR\splash.png" + +; Slow fade (step 5, ~1530ms) +nsSplashPNG::show 5000 /FADEIN 5 /FADEOUT 5 "$EXEDIR\splash.png" + +; Instant (step 255, ~30ms) +nsSplashPNG::show 3000 /FADEIN 255 /FADEOUT 255 "$EXEDIR\splash.png" + +; Different speeds for fade-in and fade-out +nsSplashPNG::show 3000 /FADEIN 51 /FADEOUT 5 "$EXEDIR\splash.png" +``` + +#### Manual Control +```nsis +; CRITICAL: Use /NOUNLOAD when using stop() to close manually +nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL "$EXEDIR\splash.png" + +; ... do installation work ... + +; Close with fade out +nsSplashPNG::stop /FADEOUT + +; Or close with custom fade speed +nsSplashPNG::stop /FADEOUT 51 +``` + +#### Real Installer Example +```nsis +Name "My Application" +OutFile "setup.exe" +InstallDir "$PROGRAMFILES\MyApp" + +!addplugindir "plugins\x86-unicode" + +Page instfiles + +Section "Main Installation" + ; Show splash with fade in, prevent user closing, no auto-close + ; CRITICAL: /NOUNLOAD required for stop() to work + nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL /FADEIN "$EXEDIR\splash.png" + + ; Install files + SetOutPath "$INSTDIR" + File /r "app\*.*" + + ; Create shortcuts + CreateDirectory "$SMPROGRAMS\MyApp" + CreateShortcut "$SMPROGRAMS\MyApp\MyApp.lnk" "$INSTDIR\myapp.exe" + + ; Close splash with fade out + nsSplashPNG::stop /FADEOUT +SectionEnd +``` + +#### Additional Examples + +See the `examples/` folder for more usage demonstrations: +- **example_basic.nsi** - Simple splash with auto-close timer +- **example_advanced.nsi** - Manual control with fade effects +- **example_formats.nsi** - Multi-format support (PNG, JPEG, BMP, GIF) +- **example_fade_speed.nsi** - Customizable fade speed demonstrations +- **example_nonblocking.nsi** - Non-blocking behavior with background processing + +## Technical Details + +### Implementation +- **Graphics Library**: WIC (Windows Imaging Component) via windowscodecs.lib +- **Image Format**: 32bppPBGRA (pre-multiplied alpha) for proper transparency +- **Window Type**: Layered windows with `WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW` +- **Transparency**: `UpdateLayeredWindow` with per-pixel alpha blending +- **Rendering**: Hardware-accelerated via `UpdateLayeredWindow` with `ULW_ALPHA` +- **Animation**: 30ms timer intervals for smooth fade (0-255 alpha range) + - Configurable step size (default: 15 steps = ~510ms duration) + - Duration formula: `(255 / step) × 30ms` +- **Threading**: Background window thread for non-blocking execution + - `show()` returns after fade-in completes + - Window stays open in background + - `stop()` waits for fade-out to complete before returning +- **Plugin State**: Requires `/NOUNLOAD` to maintain global variables (window handles, threads) between calls + +### Comparison with nsAdvsplash + +| Feature | nsAdvsplash | nsSplashPNG | +|----------------------|-------------|-----------------| +| PNG Alpha Support | Partial | Full | +| Fade Effects | Yes | Yes | +| Transparency Method | Color key | Per-pixel alpha | +| Graphics API | GDI | WIC | +| Window Type | Standard | Layered | +| Click-to-Close | Yes | Yes | +| Compilation Warnings | Some | None | + +### Supported Image Formats + +WIC automatically detects and decodes the following formats: +- **PNG** - Full alpha transparency support +- **JPEG/JPG** - No transparency (opaque) +- **BMP** - Alpha transparency if 32-bit, otherwise opaque +- **GIF** - Basic transparency support +- **TIFF** - Alpha transparency if supported by variant +- **ICO** - Icon format with alpha +- **WMP** - Windows Media Photo + +**Note**: Formats without alpha channels (JPEG, 24-bit BMP) will display as opaque images. + +### System Requirements +- Windows Vista or later (for layered window support) +- WIC - Windows Imaging Component (included with Windows Vista+) + +## API Reference + +### Exported Functions + +#### `show()` +Creates and displays a splash screen window in a background thread. + +**Parameters** (parsed from NSIS stack in reverse order): +1. Timeout in milliseconds (integer, 0 = manual close only) +2. `/FADEIN [step]` - Optional fade in with configurable step (1-255, default: 15) +3. `/FADEOUT [step]` - Optional auto fade out with configurable step (1-255, default: 15) +4. `/NOCANCEL` - Disable click-to-close +5. `/NOUNLOAD` - **Required for stop() to work**: Keeps plugin in memory +6. Image path (string) - last parameter + +**Behavior**: +- Cleans up any previous window/thread +- Loads image with WIC (`IWICImagingFactory`, `IWICBitmapDecoder`) +- Supports PNG, JPEG, BMP, GIF, TIFF, ICO, WMP formats +- Converts to 32bppPBGRA format for proper alpha handling +- Creates centered topmost layered window +- Uses `UpdateLayeredWindow` for per-pixel alpha transparency +- Starts background thread with window message loop +- Applies fade in effect if `/FADEIN` specified (30ms timer with configurable steps) +- Sets auto-close timer if timeout > 0 +- Enables click-to-close unless `/NOCANCEL` specified +- **Returns control after fade-in completes** (non-blocking) +- Without `/NOUNLOAD`, plugin unloads and loses all state + +**Return value**: Pushes "success" to NSIS stack + +#### `stop()` +Closes the splash screen window with optional fade-out. + +**Parameters** (parsed from NSIS stack): +1. `/FADEOUT [step]` - Optional fade out with configurable step (1-255, default: 15) + +**Behavior**: +- If `/FADEOUT` specified: + - Starts fade-out timer (30ms intervals) + - Waits for fade to complete: `(255 / step) × 30ms` + - Destroys window after fade completes +- Otherwise: + - Immediately destroys window +- **Requires show() to have used /NOUNLOAD** or window handle will be NULL + +**Return value**: Pushes "success" to NSIS stack + +**Behavior**: +- Applies fade out effect if `/FADEOUT` specified (30ms timer, 15 steps) +- Destroys window and releases WIC resources +- Terminates splash thread and cleans up handles + +## Troubleshooting + +### Image not showing +- Verify image file path is correct and accessible +- Check that image file is in a supported format (PNG, JPEG, BMP, GIF, TIFF, ICO) +- Ensure NSIS can read the file at install time +- Verify WIC can decode the specific image file variant + +### No transparency +- Confirm PNG has alpha channel (not just RGB) +- Verify you're using the correct plugin DLL for your NSIS version +- Check Windows version supports layered windows (Vista+) + +### Fade effects not smooth +- Normal behavior - fade uses 30ms timer with 15-step alpha increments +- Total fade duration ≈ 450-500ms + +## License + +Created for NSIS installer system. + +## Credits + +- NSIS Plugin API +- WIC - Windows Imaging Component +- Windows Layered Windows API (`UpdateLayeredWindow`) +- Build system inspired by nsProcess plugin + +## Version History + +### 1.0.0 (Current) +- Full PNG alpha transparency support via WIC +- Per-pixel alpha blending with `UpdateLayeredWindow` +- Fade in/out effects (30ms, 15 steps) +- Auto-close timer with configurable timeout +- Click-to-close with optional disable (`/NOCANCEL`) +- Blocks installer until splash closes +- x86-unicode, x64-unicode, and x86-ansi builds +- Zero compilation warnings with guarded macro definitions +- Optimized build system with parallel compilation support diff --git a/examples/example_advanced.nsi b/examples/example_advanced.nsi new file mode 100644 index 0000000..961fd46 --- /dev/null +++ b/examples/example_advanced.nsi @@ -0,0 +1,47 @@ +; nsSplashPNG Advanced Example +; Shows how to use nsSplashPNG in a real installer with progress updates + +Name "nsSplashPNG Advanced Test" +OutFile "nsSplashPNG_Advanced.exe" +InstallDir "$PROGRAMFILES\TestApp" +RequestExecutionLevel admin + +!addplugindir "..\plugins\x86-unicode" + +Page instfiles + +Section "Main Installation" + ; Copy splash image to temp directory + SetOutPath $TEMP + File "images\splash.png" + + ; Show splash with fade in, no cancel button + ; CRITICAL: /NOUNLOAD required since we call stop() later + nsSplashPNG::show /NOUNLOAD 0 /NOCANCEL /FADEIN "$TEMP\splash.png" + + ; Simulate installation steps + DetailPrint "Installing files..." + SetOutPath "$INSTDIR" + Sleep 1000 + + DetailPrint "Configuring application..." + Sleep 1000 + + DetailPrint "Creating shortcuts..." + Sleep 1000 + + DetailPrint "Finalizing installation..." + Sleep 1000 + + ; Close splash with fade out + nsSplashPNG::stop /FADEOUT + + ; Clean up + Delete "$TEMP\splash.png" + + DetailPrint "Installation complete!" +SectionEnd + +Section "Uninstall" + RMDir /r "$INSTDIR" +SectionEnd diff --git a/examples/example_basic.nsi b/examples/example_basic.nsi new file mode 100644 index 0000000..878881c --- /dev/null +++ b/examples/example_basic.nsi @@ -0,0 +1,39 @@ +; nsSplashPNG Example Script +; Demonstrates the usage of the nsSplashPNG plugin with various options + +Name "nsSplashPNG Test" +OutFile "nsSplashPNG_Example.exe" +SilentInstall silent +RequestExecutionLevel user + +!addplugindir "..\plugins\x86-unicode" + +Section + ; Copy splash image to temp directory + SetOutPath $TEMP + File "images\splash.png" + + ; Example 1: Simple splash with auto-close after 3 seconds + ; nsSplashPNG::show 3000 "test_image.png" + + ; Example 2: Splash with fade in effect, auto-close after 3 seconds + ; nsSplashPNG::show /FADEIN 3000 "test_image.png" + + ; Example 3: Splash with fade out effect, auto-close after 3 seconds + ; nsSplashPNG::show /FADEOUT 3000 "test_image.png" + + ; Example 4: Splash with fade in, manual close (no auto-close) + ; CRITICAL: /NOUNLOAD required since we call stop() later + nsSplashPNG::show /NOUNLOAD 0 /FADEIN "$TEMP\splash.png" + + ; Simulate some installation work + Sleep 2000 + + ; Example 5: Manual close with fade out + nsSplashPNG::stop /FADEOUT + + ; Clean up + Delete "$TEMP\splash.png" + + MessageBox MB_OK "Installation complete!" +SectionEnd diff --git a/examples/example_fade_speed.nsi b/examples/example_fade_speed.nsi new file mode 100644 index 0000000..303d63b --- /dev/null +++ b/examples/example_fade_speed.nsi @@ -0,0 +1,50 @@ +; Test fade speed customization +Unicode true +!addplugindir "N:\Code\Workspace\Launchers\plugins\x86-unicode" + +Name "Fade Speed Test" +OutFile "test_fade_speed.exe" +SilentInstall silent +RequestExecutionLevel user + +Function .onInit + SetOutPath $TEMP + File "images\splash.png" + + ; Test 1: Default speed (step 15, ~510ms fade) + MessageBox MB_OK "Test 1: Default fade speed (step 15)$\n$\nFade duration: ~510ms" + nssplashpng::show /NOUNLOAD 60000 /FADEIN "$TEMP\splash.png" + Pop $0 + Sleep 3000 + nssplashpng::stop /FADEOUT + ; No need to sleep - stop now waits for fade-out internally + + ; Test 2: Fast fade (step 51, ~150ms fade) + MessageBox MB_OK "Test 2: Fast fade (step 51)$\n$\nFade duration: ~150ms" + nssplashpng::show /NOUNLOAD 60000 /FADEIN 51 "$TEMP\splash.png" + Pop $0 + Sleep 3000 + nssplashpng::stop /FADEOUT 51 + ; No need to sleep - stop now waits for fade-out internally + + ; Test 3: Slow fade (step 5, ~1530ms fade) + MessageBox MB_OK "Test 3: Slow fade (step 5)$\n$\nFade duration: ~1530ms" + nssplashpng::show /NOUNLOAD 60000 /FADEIN 5 "$TEMP\splash.png" + Pop $0 + Sleep 5000 + nssplashpng::stop /FADEOUT 5 + ; No need to sleep - stop now waits for fade-out internally + + ; Test 4: Instant (step 255, ~30ms fade) + MessageBox MB_OK "Test 4: Instant fade (step 255)$\n$\nFade duration: ~30ms" + nssplashpng::show /NOUNLOAD 60000 /FADEIN 255 "$TEMP\splash.png" + Pop $0 + Sleep 2000 + nssplashpng::stop /FADEOUT 255 + ; No need to sleep - stop now waits for fade-out internally Delete "$TEMP\splash.png" + MessageBox MB_OK "All fade speed tests completed!" + Quit +FunctionEnd + +Section +SectionEnd diff --git a/examples/example_formats.nsi b/examples/example_formats.nsi new file mode 100644 index 0000000..aba4ea5 --- /dev/null +++ b/examples/example_formats.nsi @@ -0,0 +1,43 @@ +; Test different image formats with nsSplashPNG +Unicode true + +Name "Format Test" +OutFile "test_formats.exe" +InstallDir "$TEMP\test" + +!addplugindir "N:\Code\Workspace\Launchers\plugins\x86-unicode" + +Section + SetOutPath $TEMP + File "images\splash.png" + File "images\splash.jpg" + File "images\splash.bmp" + File "images\splash.gif" + + ; Test PNG (with alpha) + MessageBox MB_OK "Testing PNG format (with alpha transparency)" + nssplashpng::show /NOUNLOAD 2000 /FADEIN /FADEOUT "$TEMP\splash.png" + Pop $0 + + ; Test JPEG (no alpha) + MessageBox MB_OK "Testing JPEG format (no transparency)" + nssplashpng::show /NOUNLOAD 2000 /FADEIN /FADEOUT "$TEMP\splash.jpg" + Pop $0 + + ; Test BMP (no alpha) + MessageBox MB_OK "Testing BMP format (no transparency)" + nssplashpng::show /NOUNLOAD 2000 /FADEIN /FADEOUT "$TEMP\splash.bmp" + Pop $0 + + ; Test GIF (with transparency) + MessageBox MB_OK "Testing GIF format (basic transparency)" + nssplashpng::show /NOUNLOAD 2000 /FADEIN /FADEOUT "$TEMP\splash.gif" + Pop $0 + + Delete "$TEMP\splash.png" + Delete "$TEMP\splash.jpg" + Delete "$TEMP\splash.bmp" + Delete "$TEMP\splash.gif" + + MessageBox MB_OK "All format tests completed!" +SectionEnd diff --git a/examples/example_nonblocking.nsi b/examples/example_nonblocking.nsi new file mode 100644 index 0000000..77bedbb --- /dev/null +++ b/examples/example_nonblocking.nsi @@ -0,0 +1,34 @@ +; Test non-blocking behavior +Unicode true +!addplugindir "N:\Code\Workspace\Launchers\plugins\x86-unicode" + +Name "Non-Blocking Test" +OutFile "test_nonblocking.exe" +SilentInstall silent +RequestExecutionLevel user + +Function .onInit + SetOutPath $TEMP + File "images\splash.png" + + ; Show splash with fade-in, manual close (0 timeout) + ; CRITICAL: /NOUNLOAD required since we call stop() later + MessageBox MB_OK "Showing splash with fade-in. Script should continue immediately after fade-in completes." + nssplashpng::show /NOUNLOAD 0 /FADEIN "$TEMP\splash.png" + Pop $0 + + ; This should appear right after fade-in, while splash is still visible + MessageBox MB_OK "Splash is visible! Result: $0$\n$\nNow doing some work..." + Sleep 2000 + + MessageBox MB_OK "Work done. Now closing splash with fade-out..." + nssplashpng::stop /FADEOUT + Pop $0 + + MessageBox MB_OK "Splash closed! Stop result: $0" + Delete "$TEMP\splash.png" + Quit +FunctionEnd + +Section +SectionEnd diff --git a/examples/images/splash.bmp b/examples/images/splash.bmp new file mode 100644 index 0000000000000000000000000000000000000000..32c2beed79e4996e08a3d56328bd19f813f88c21 GIT binary patch literal 262282 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 LlGf-40Eqt&EaRnc literal 0 HcmV?d00001 diff --git a/examples/images/splash.jpg b/examples/images/splash.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89d95d260e6c51fc34fd3a1fc65cedbd9e44c264 GIT binary patch literal 13543 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 +#include +#include +#include +#include +#include "pluginapi.h" + +#pragma comment(lib, "windowscodecs.lib") +#pragma comment(lib, "ole32.lib") + +// Constants +#define RESOLUTION 30 // 30ms timer for smooth animation +#define FADEIN_DURATION 500 // 500ms fade in (255 / 15 * 30ms ≈ 510ms) +#define FADEOUT_DURATION 500 // 500ms fade out +#define CLASS_NAME _T("_nsSplashPNG") + +// Global variables +HWND g_hWnd = NULL; +HINSTANCE g_hInstance = NULL; +HBITMAP g_hBitmap = NULL; +HDC g_hdcMem = NULL; +int g_imageWidth = 0; +int g_imageHeight = 0; +int g_timeoutMs = 0; +int g_fadeAlpha = 0; +bool g_fadeIn = false; +bool g_fadeOut = false; +bool g_noCancel = false; +int g_fadeInStep = 15; // Default: 15 alpha units per timer tick +int g_fadeOutStep = 15; // Default: 15 alpha units per timer tick +HANDLE g_hThread = NULL; +HANDLE g_hEvent = NULL; + +// Monitor selection +#define MONITOR_MODE_PRIMARY 0 // Primary monitor (default) +#define MONITOR_MODE_MOUSE 1 // Monitor where mouse cursor is +#define MONITOR_MODE_INDEX 2 // Monitor by 1-based index +#define MONITOR_MODE_POINT 3 // Monitor nearest to a specific screen point +int g_monitorMode = MONITOR_MODE_PRIMARY; +int g_monitorIndex = 0; // 0-based index used internally +int g_monitorX = 0; // Screen X coordinate for MONITOR_MODE_POINT +int g_monitorY = 0; // Screen Y coordinate for MONITOR_MODE_POINT + +struct MonitorEnumData { + int targetIndex; + int currentIndex; + HMONITOR hMonitor; + MONITORINFO info; +}; + +static BOOL CALLBACK GetMonitorByIndexProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) { + MonitorEnumData* pData = (MonitorEnumData*)dwData; + if (pData->currentIndex == pData->targetIndex) { + pData->hMonitor = hMonitor; + pData->info.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(hMonitor, &pData->info); + return FALSE; // Stop enumeration + } + pData->currentIndex++; + return TRUE; +} + +typedef BOOL (WINAPI *SetLayeredWindowAttributesProc)(HWND, COLORREF, BYTE, DWORD); +SetLayeredWindowAttributesProc g_SetLayeredWindowAttributes = NULL; + +// Window procedure +LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_CREATE: + return 0; + + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + // Don't draw anything - layered window uses UpdateLayeredWindow + EndPaint(hwnd, &ps); + return 0; + } + + case WM_LBUTTONDOWN: + if (g_noCancel) + return 0; + // Intentional fallthrough to close splash on click + DestroyWindow(hwnd); + return 0; + + case WM_TIMER: + { + if (wParam == 1) { // Fade in timer + if (g_fadeAlpha < 255) { + g_fadeAlpha += g_fadeInStep; + if (g_fadeAlpha > 255) g_fadeAlpha = 255; + + // Update alpha using UpdateLayeredWindow + if (g_hBitmap && g_hdcMem) { + HDC hdcScreen = GetDC(NULL); + POINT ptSrc = {0, 0}; + SIZE sizeWnd = {g_imageWidth, g_imageHeight}; + BLENDFUNCTION blend = {AC_SRC_OVER, 0, (BYTE)g_fadeAlpha, AC_SRC_ALPHA}; + UpdateLayeredWindow(hwnd, hdcScreen, NULL, &sizeWnd, g_hdcMem, &ptSrc, 0, &blend, ULW_ALPHA); + ReleaseDC(NULL, hdcScreen); + } + } else { + KillTimer(hwnd, 1); + } + } else if (wParam == 2) { // Close timer + KillTimer(hwnd, 2); + if (g_fadeOut) { + // Start fade out + SetTimer(hwnd, 3, RESOLUTION, NULL); + } else { + DestroyWindow(hwnd); + } + } else if (wParam == 3) { // Fade out timer + if (g_fadeAlpha > 0) { + g_fadeAlpha -= g_fadeOutStep; + if (g_fadeAlpha < 0) g_fadeAlpha = 0; + + // Update alpha using UpdateLayeredWindow + if (g_hBitmap && g_hdcMem) { + HDC hdcScreen = GetDC(NULL); + POINT ptSrc = {0, 0}; + SIZE sizeWnd = {g_imageWidth, g_imageHeight}; + BLENDFUNCTION blend = {AC_SRC_OVER, 0, (BYTE)g_fadeAlpha, AC_SRC_ALPHA}; + UpdateLayeredWindow(hwnd, hdcScreen, NULL, &sizeWnd, g_hdcMem, &ptSrc, 0, &blend, ULW_ALPHA); + ReleaseDC(NULL, hdcScreen); + } + } else { + KillTimer(hwnd, 3); + DestroyWindow(hwnd); + } + } + return 0; + } + + case WM_CLOSE: + return 0; + + case WM_DESTROY: + { + if (g_hdcMem) { + DeleteDC(g_hdcMem); + g_hdcMem = NULL; + } + if (g_hBitmap) { + DeleteObject(g_hBitmap); + g_hBitmap = NULL; + } + g_hWnd = NULL; + PostQuitMessage(0); + return 0; + } + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +// DLL entry point +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { + if (dwReason == DLL_PROCESS_ATTACH) { + g_hInstance = hInstance; + // This is acceptable for NSIS plugins which are loaded/unloaded by the installer process + } + return TRUE; +} + +// Thread function to create and manage the splash window +DWORD WINAPI SplashThreadProc(LPVOID lpParam) { + // Register window class + WNDCLASS wc = {0}; + wc.lpfnWndProc = WndProc; + wc.hInstance = g_hInstance; + wc.lpszClassName = CLASS_NAME; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + RegisterClass(&wc); + + // Calculate centered position on the target monitor + RECT monitorRect = {0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)}; + if (g_monitorMode == MONITOR_MODE_MOUSE) { + POINT pt; + if (GetCursorPos(&pt)) { + HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + MONITORINFO mi = {sizeof(mi)}; + if (GetMonitorInfo(hMon, &mi)) + monitorRect = mi.rcMonitor; + } + } else if (g_monitorMode == MONITOR_MODE_INDEX) { + MonitorEnumData data = {}; + data.targetIndex = g_monitorIndex; + data.info.cbSize = sizeof(MONITORINFO); + EnumDisplayMonitors(NULL, NULL, GetMonitorByIndexProc, (LPARAM)&data); + if (data.hMonitor) + monitorRect = data.info.rcMonitor; + } else if (g_monitorMode == MONITOR_MODE_POINT) { + POINT pt = {g_monitorX, g_monitorY}; + HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + MONITORINFO mi = {sizeof(mi)}; + if (GetMonitorInfo(hMon, &mi)) + monitorRect = mi.rcMonitor; + } + int x = monitorRect.left + (monitorRect.right - monitorRect.left - g_imageWidth) / 2; + int y = monitorRect.top + (monitorRect.bottom - monitorRect.top - g_imageHeight) / 2; + + // Create layered window + g_hWnd = CreateWindowEx( + WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, + CLASS_NAME, + _T(""), + WS_POPUP, + x, y, g_imageWidth, g_imageHeight, + NULL, + NULL, + g_hInstance, + NULL + ); + + if (!g_hWnd) { + SetEvent(g_hEvent); + return 1; + } + + // Display using UpdateLayeredWindow + g_fadeAlpha = g_fadeIn ? 0 : 255; + + HDC hdcScreen = GetDC(NULL); + POINT ptSrc = {0, 0}; + SIZE sizeWnd = {g_imageWidth, g_imageHeight}; + BLENDFUNCTION blend = {AC_SRC_OVER, 0, (BYTE)g_fadeAlpha, AC_SRC_ALPHA}; + UpdateLayeredWindow(g_hWnd, hdcScreen, NULL, &sizeWnd, g_hdcMem, &ptSrc, 0, &blend, ULW_ALPHA); + ReleaseDC(NULL, hdcScreen); + + ShowWindow(g_hWnd, SW_SHOW); + SetForegroundWindow(g_hWnd); + + // Start timers + if (g_fadeIn) { + SetTimer(g_hWnd, 1, RESOLUTION, NULL); + } + if (g_timeoutMs > 0) { + SetTimer(g_hWnd, 2, g_timeoutMs, NULL); + } + + // Signal that window is created + SetEvent(g_hEvent); + + // Message loop + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Cleanup + UnregisterClass(CLASS_NAME, g_hInstance); + return 0; +} + +// Load PNG using Windows Imaging Component (WIC) +HBITMAP LoadPNGWithWIC(const TCHAR* filename, int* width, int* height) { + + HRESULT hr; + IWICImagingFactory* pFactory = NULL; + IWICBitmapDecoder* pDecoder = NULL; + IWICBitmapFrameDecode* pFrame = NULL; + IWICFormatConverter* pConverter = NULL; + HBITMAP hBitmap = NULL; + + // Initialize COM (don't check failure - might already be initialized by NSIS) + CoInitialize(NULL); + + hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, (LPVOID*)&pFactory); + if (FAILED(hr)) { + return NULL; + } + + hr = pFactory->CreateDecoderFromFilename((LPCWSTR)filename, NULL, GENERIC_READ, + WICDecodeMetadataCacheOnLoad, &pDecoder); + if (FAILED(hr)) goto cleanup; + + hr = pDecoder->GetFrame(0, &pFrame); + if (FAILED(hr)) goto cleanup; + + UINT imgWidth, imgHeight; + pFrame->GetSize(&imgWidth, &imgHeight); + *width = imgWidth; + *height = imgHeight; + + hr = pFactory->CreateFormatConverter(&pConverter); + if (FAILED(hr)) goto cleanup; + + hr = pConverter->Initialize(pFrame, GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, NULL, 0.0, + WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) goto cleanup; + + // Declare variables before goto + BITMAPINFO bmi; + ZeroMemory(&bmi, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = imgWidth; + bmi.bmiHeader.biHeight = -(int)imgHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + void* pBits; + pBits = NULL; + HDC hdc; + hdc = GetDC(NULL); + hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, NULL, 0); + ReleaseDC(NULL, hdc); + + if (!hBitmap) goto cleanup; + + hr = pConverter->CopyPixels(NULL, imgWidth * 4, imgWidth * imgHeight * 4, (BYTE*)pBits); + if (FAILED(hr)) { + DeleteObject(hBitmap); + hBitmap = NULL; + } + +cleanup: + if (pConverter) pConverter->Release(); + if (pFrame) pFrame->Release(); + if (pDecoder) pDecoder->Release(); + if (pFactory) pFactory->Release(); + // Don't call CoUninitialize - let NSIS handle COM cleanup + // CoUninitialize(); + + return hBitmap; +} + +// Plugin exported function: show +extern "C" void __declspec(dllexport) show(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) { + EXDLL_INIT(); + + // Close any existing window and thread first + if (g_hWnd && IsWindow(g_hWnd)) { + DestroyWindow(g_hWnd); + // Give thread time to process WM_DESTROY and exit message loop + Sleep(200); + } + + // Wait for previous thread to terminate if it exists + if (g_hThread) { + // Wait briefly for thread to terminate + WaitForSingleObject(g_hThread, 500); + CloseHandle(g_hThread); + g_hThread = NULL; + } + + // Ensure window handle is cleared + g_hWnd = NULL; + + // Parameters come from stack in order: timeout, flags..., imagepath + // Read timeout first + TCHAR param[MAX_PATH]; + TCHAR imagePath[MAX_PATH] = {0}; + + popstring(param); + g_timeoutMs = myatoi(param); + + // Reset flags + g_fadeIn = false; + g_fadeOut = false; + g_noCancel = false; + g_fadeInStep = 15; // Default step + g_fadeOutStep = 15; // Default step + g_monitorMode = MONITOR_MODE_PRIMARY; + g_monitorIndex = 0; + g_monitorX = 0; + g_monitorY = 0; + + // Read flags and image path + while (popstring(param) == 0 && param[0] == _T('/')) { + if (_tcsicmp(param, _T("/FADEIN")) == 0) { + g_fadeIn = true; + // Check if next parameter is a number (custom step) + TCHAR nextParam[MAX_PATH]; + if (popstring(nextParam) == 0 && nextParam[0] != _T('/')) { + int customStep = myatoi(nextParam); + if (customStep > 0 && customStep <= 255) { + g_fadeInStep = customStep; + } else { + // Not a valid step, push it back (it's probably the image path) + lstrcpy(param, nextParam); + break; + } + } else { + // Push it back for next iteration + lstrcpy(param, nextParam); + } + } else if (_tcsicmp(param, _T("/FADEOUT")) == 0) { + g_fadeOut = true; + // Check if next parameter is a number (custom step) + TCHAR nextParam[MAX_PATH]; + if (popstring(nextParam) == 0 && nextParam[0] != _T('/')) { + int customStep = myatoi(nextParam); + if (customStep > 0 && customStep <= 255) { + g_fadeOutStep = customStep; + } else { + // Not a valid step, push it back + lstrcpy(param, nextParam); + break; + } + } else { + // Push it back for next iteration + lstrcpy(param, nextParam); + } + } else if (_tcsicmp(param, _T("/NOCANCEL")) == 0) { + g_noCancel = true; + } else if (_tcsicmp(param, _T("/MONITOR")) == 0) { + TCHAR monParam[64] = {0}; + if (popstring(monParam) == 0) { + if (_tcsicmp(monParam, _T("CURRENT")) == 0 || _tcsicmp(monParam, _T("MOUSE")) == 0) { + g_monitorMode = MONITOR_MODE_MOUSE; + } else if (_tcsicmp(monParam, _T("PRIMARY")) == 0) { + g_monitorMode = MONITOR_MODE_PRIMARY; + } else if (_tcsicmp(monParam, _T("POINT")) == 0) { + TCHAR xParam[64] = {0}, yParam[64] = {0}; + if (popstring(xParam) == 0 && popstring(yParam) == 0) { + g_monitorMode = MONITOR_MODE_POINT; + g_monitorX = myatoi(xParam); + g_monitorY = myatoi(yParam); + } + } else { + int idx = myatoi(monParam); + if (idx >= 1) { + g_monitorMode = MONITOR_MODE_INDEX; + g_monitorIndex = idx - 1; // Convert to 0-based + } + } + } + } + } + + // Last parameter read (not starting with /) is the image path + lstrcpy(imagePath, param); + + // Validate image path + if (imagePath[0] == 0) { + pushstring(_T("error: no image path")); + return; + } + + // Load PNG image with WIC + g_hBitmap = LoadPNGWithWIC(imagePath, &g_imageWidth, &g_imageHeight); + if (!g_hBitmap) { + pushstring(_T("error: cannot load image")); + return; + } + + // Create memory DC for the bitmap + HDC hdc = GetDC(NULL); + g_hdcMem = CreateCompatibleDC(hdc); + ReleaseDC(NULL, hdc); + SelectObject(g_hdcMem, g_hBitmap); + + // Create event for synchronization + g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!g_hEvent) { + pushstring(_T("error: cannot create event")); + if (g_hdcMem) DeleteDC(g_hdcMem); + if (g_hBitmap) DeleteObject(g_hBitmap); + return; + } + + // Create thread to manage the window + g_hThread = CreateThread(NULL, 0, SplashThreadProc, NULL, 0, NULL); + if (!g_hThread) { + pushstring(_T("error: cannot create thread")); + CloseHandle(g_hEvent); + if (g_hdcMem) DeleteDC(g_hdcMem); + if (g_hBitmap) DeleteObject(g_hBitmap); + return; + } + + // Wait for window to be created (with timeout) + DWORD result = WaitForSingleObject(g_hEvent, 2000); + CloseHandle(g_hEvent); + g_hEvent = NULL; + + if (result != WAIT_OBJECT_0 || !g_hWnd) { + pushstring(_T("error: window not created")); + return; + } + + // If fade-in is enabled, wait for it to complete before returning + if (g_fadeIn) { + // Calculate fade-in duration: (255 / step) * 30ms + 100ms margin + int fadeSteps = (255 + g_fadeInStep - 1) / g_fadeInStep; // Round up + int fadeDuration = fadeSteps * RESOLUTION + 100; + Sleep(fadeDuration); + } + + // Return control to NSIS immediately - window stays open in background thread + // Window will close either: + // 1. When timeout expires (if g_timeoutMs > 0) + // 2. When user clicks (if !g_noCancel) + // 3. When stop() is called explicitly + pushstring(_T("success")); +} + +// Plugin exported function: stop +extern "C" void __declspec(dllexport) stop(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) { + EXDLL_INIT(); + + // Parse flags + TCHAR param[256] = {0}; + bool fadeOut = false; + + while (popstring(param) == 0) { + if (_tcsicmp(param, _T("/FADEOUT")) == 0) { + fadeOut = true; + // Check if next parameter is a number (custom step) + TCHAR nextParam[MAX_PATH]; + if (popstring(nextParam) == 0 && nextParam[0] != _T('/')) { + int customStep = myatoi(nextParam); + if (customStep > 0 && customStep <= 255) { + g_fadeOutStep = customStep; + } + } + } + } + + if (g_hWnd && IsWindow(g_hWnd)) { + if (fadeOut) { + // Start fade out timer + SetTimer(g_hWnd, 3, RESOLUTION, NULL); + + // Calculate expected fade-out duration and wait + int fadeSteps = (255 + g_fadeOutStep - 1) / g_fadeOutStep; + int fadeDuration = fadeSteps * RESOLUTION + 200; + + Sleep(fadeDuration); + + // Give extra time for window destruction and thread cleanup + Sleep(100); + } else { + // No fade-out, destroy immediately + DestroyWindow(g_hWnd); + Sleep(100); // Give thread time to cleanup + } + } + + pushstring(_T("success")); +} diff --git a/src/api.h b/src/api.h new file mode 100644 index 0000000..85d41c4 --- /dev/null +++ b/src/api.h @@ -0,0 +1,83 @@ +/* + * apih + * + * This file is a part of NSIS. + * + * Copyright (C) 1999-2013 Nullsoft and Contributors + * + * Licensed under the zlib/libpng license (the "License"); + * you may not use this file except in compliance with the License. + * + * Licence details can be found in the file COPYING. + * + * This software is provided 'as-is', without any express or implied + * warranty. + */ + +#ifndef _NSIS_EXEHEAD_API_H_ +#define _NSIS_EXEHEAD_API_H_ + +// Starting with NSIS 2.42, you can check the version of the plugin API in exec_flags->plugin_api_version +// The format is 0xXXXXYYYY where X is the major version and Y is the minor version (MAKELONG(y,x)) +// When doing version checks, always remember to use >=, ex: if (pX->exec_flags->plugin_api_version >= NSISPIAPIVER_1_0) {} + +#define NSISPIAPIVER_1_0 0x00010000 +#define NSISPIAPIVER_CURR NSISPIAPIVER_1_0 + +// NSIS Plug-In Callback Messages +enum NSPIM +{ + NSPIM_UNLOAD, // This is the last message a plugin gets, do final cleanup + NSPIM_GUIUNLOAD, // Called after .onGUIEnd +}; + +// Prototype for callbacks registered with extra_parameters->RegisterPluginCallback() +// Return NULL for unknown messages +// Should always be __cdecl for future expansion possibilities +typedef UINT_PTR (*NSISPLUGINCALLBACK)(enum NSPIM); + +// extra_parameters data structures containing other interesting stuff +// but the stack, variables and HWND passed on to plug-ins. +typedef struct +{ + int autoclose; + int all_user_var; + int exec_error; + int abort; + int exec_reboot; // NSIS_SUPPORT_REBOOT + int reboot_called; // NSIS_SUPPORT_REBOOT + int XXX_cur_insttype; // depreacted + int plugin_api_version; // see NSISPIAPIVER_CURR + // used to be XXX_insttype_changed + int silent; // NSIS_CONFIG_SILENT_SUPPORT + int instdir_error; + int rtl; + int errlvl; + int alter_reg_view; + int status_update; +} exec_flags_t; + +#ifndef NSISCALL +# define NSISCALL __stdcall +#endif + +typedef struct { + exec_flags_t *exec_flags; + int (NSISCALL *ExecuteCodeSegment)(int, HWND); + void (NSISCALL *validate_filename)(TCHAR *); + int (NSISCALL *RegisterPluginCallback)(HMODULE, NSISPLUGINCALLBACK); // returns 0 on success, 1 if already registered and < 0 on errors +} extra_parameters; + +// Definitions for page showing plug-ins +// See Ui.c to understand better how they're used + +// sent to the outer window to tell it to go to the next inner window +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) + +// custom pages should send this message to let NSIS know they're ready +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) + +// sent as wParam with WM_NOTIFY_OUTER_NEXT when user cancels - heed its warning +#define NOTIFY_BYE_BYE 'x' + +#endif /* _PLUGIN_H_ */ diff --git a/src/nsSplashPNG.def b/src/nsSplashPNG.def new file mode 100644 index 0000000..8293bdb --- /dev/null +++ b/src/nsSplashPNG.def @@ -0,0 +1,4 @@ +LIBRARY nsSplashPNG +EXPORTS + show @1 + stop @2 diff --git a/src/nsSplashPNG.vcxproj b/src/nsSplashPNG.vcxproj new file mode 100644 index 0000000..413d9d7 --- /dev/null +++ b/src/nsSplashPNG.vcxproj @@ -0,0 +1,187 @@ + + + + + Release + Win32 + + + Release Unicode + Win32 + + + Release + x64 + + + Release Unicode + x64 + + + + 17.0 + {B8F3A9D1-4C5E-4F2A-9B1C-8D7E6F5A4B3C} + Win32Proj + nsSplashPNG + 10.0 + + + + DynamicLibrary + false + v143 + true + MultiByte + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + false + v143 + true + MultiByte + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + true + false + + WIN32;NDEBUG;_WINDOWS;_USRDLL;EXDLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + MaxSpeed + + + Windows + true + true + false + + kernel32.lib;user32.lib;gdi32.lib;ole32.lib;vfw32.lib;winmm.lib;%(AdditionalDependencies) + nsSplashPNG.def + + + + + Level3 + true + true + false + + WIN32;NDEBUG;_WINDOWS;_USRDLL;EXDLL_EXPORTS;UNICODE;_UNICODE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + MaxSpeed + + + Windows + true + true + false + + kernel32.lib;user32.lib;gdi32.lib;ole32.lib;vfw32.lib;winmm.lib;%(AdditionalDependencies) + nsSplashPNG.def + + + + + Level3 + true + true + false + + NDEBUG;_WINDOWS;_USRDLL;EXDLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + MaxSpeed + + + Windows + true + true + false + + kernel32.lib;user32.lib;gdi32.lib;ole32.lib;vfw32.lib;winmm.lib;%(AdditionalDependencies) + nsSplashPNG.def + + + + + Level3 + true + true + false + + NDEBUG;_WINDOWS;_USRDLL;EXDLL_EXPORTS;UNICODE;_UNICODE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + MaxSpeed + + + Windows + true + true + false + + windowscodecs.lib;ole32.lib;kernel32.lib;user32.lib;gdi32.lib;msimg32.lib;%(AdditionalDependencies) + nsSplashPNG.def + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/nsis_tchar.h b/src/nsis_tchar.h new file mode 100644 index 0000000..c818356 --- /dev/null +++ b/src/nsis_tchar.h @@ -0,0 +1,243 @@ +/* + * nsis_tchar.h + * + * This file is a part of NSIS. + * + * Copyright (C) 1999-2013 Nullsoft and Contributors + * + * This software is provided 'as-is', without any express or implied + * warranty. + * + * For Unicode support by Jim Park -- 08/30/2007 + */ + +// Jim Park: Only those we use are listed here. + +#pragma once + +#ifdef _UNICODE + +#ifndef _T +#define __T(x) L ## x +#define _T(x) __T(x) +#define _TEXT(x) __T(x) +#endif + +#ifndef _TCHAR_DEFINED +#define _TCHAR_DEFINED +#if !defined(_NATIVE_WCHAR_T_DEFINED) && !defined(_WCHAR_T_DEFINED) +typedef unsigned short TCHAR; +#else +typedef wchar_t TCHAR; +#endif +#endif + + +// program +#define _tenviron _wenviron +#define __targv __wargv + +// printfs +#define _ftprintf fwprintf +#define _sntprintf _snwprintf +#if (defined(_MSC_VER) && (_MSC_VER<=1310)) || defined(__MINGW32__) +# define _stprintf swprintf +#else +# define _stprintf _swprintf +#endif +#define _tprintf wprintf +#define _vftprintf vfwprintf +#define _vsntprintf _vsnwprintf +#if defined(_MSC_VER) && (_MSC_VER<=1310) +# define _vstprintf vswprintf +#else +# ifndef _vstprintf +# define _vstprintf _vswprintf +# endif +#endif + +// scanfs +#define _tscanf wscanf +#define _stscanf swscanf + +// string manipulations +#define _tcscat wcscat +#define _tcschr wcschr +#define _tcsclen wcslen +#define _tcscpy wcscpy +#define _tcsdup _wcsdup +#define _tcslen wcslen +#define _tcsnccpy wcsncpy +#define _tcsncpy wcsncpy +#define _tcsrchr wcsrchr +#define _tcsstr wcsstr +#ifndef _tcstok +#define _tcstok wcstok +#endif + +// string comparisons +#define _tcscmp wcscmp +#define _tcsicmp _wcsicmp +#define _tcsncicmp _wcsnicmp +#define _tcsncmp wcsncmp +#define _tcsnicmp _wcsnicmp + +// upper / lower +#define _tcslwr _wcslwr +#define _tcsupr _wcsupr +#define _totlower towlower +#define _totupper towupper + +// conversions to numbers +#define _tcstoi64 _wcstoi64 +#define _tcstol wcstol +#define _tcstoul wcstoul +#define _tstof _wtof +#define _tstoi _wtoi +#define _tstoi64 _wtoi64 +#define _ttoi _wtoi +#define _ttoi64 _wtoi64 +#define _ttol _wtol + +// conversion from numbers to strings +#define _itot _itow +#define _ltot _ltow +#define _i64tot _i64tow +#define _ui64tot _ui64tow + +// file manipulations +#define _tfopen _wfopen +#define _topen _wopen +#define _tremove _wremove +#define _tunlink _wunlink + +// reading and writing to i/o +#define _fgettc fgetwc +#define _fgetts fgetws +#define _fputts fputws +#define _gettchar getwchar + +// directory +#define _tchdir _wchdir + +// environment +#define _tgetenv _wgetenv +#define _tsystem _wsystem + +// time +#define _tcsftime wcsftime + +#else // ANSI + +#ifndef _T +#define _T(x) x +#define _TEXT(x) x +#endif + +#ifndef _TCHAR_DEFINED +#define _TCHAR_DEFINED +typedef char TCHAR; +#endif + +// program +#ifndef _tenviron +#define _tenviron environ +#endif +#define __targv __argv + +// printfs +#define _ftprintf fprintf +#define _sntprintf _snprintf +#define _stprintf sprintf +#define _tprintf printf +#define _vftprintf vfprintf +#define _vsntprintf _vsnprintf +#define _vstprintf vsprintf + +// scanfs +#define _tscanf scanf +#define _stscanf sscanf + +// string manipulations +#define _tcscat strcat +#define _tcschr strchr +#define _tcsclen strlen +#define _tcscnlen strnlen +#define _tcscpy strcpy +#define _tcsdup _strdup +#define _tcslen strlen +#define _tcsnccpy strncpy +#define _tcsrchr strrchr +#define _tcsstr strstr +#define _tcstok strtok + +// string comparisons +#define _tcscmp strcmp +#define _tcsicmp _stricmp +#define _tcsncmp strncmp +#define _tcsncicmp _strnicmp +#define _tcsnicmp _strnicmp + +// upper / lower +#define _tcslwr _strlwr +#define _tcsupr _strupr + +#ifndef _totupper +#define _totupper toupper +#endif +#ifndef _totlower +#define _totlower tolower +#endif + +// conversions to numbers +#define _tcstol strtol +#define _tcstoul strtoul +#define _tstof atof +#define _tstoi atoi +#define _tstoi64 _atoi64 +#define _tstoi64 _atoi64 +#define _ttoi atoi +#define _ttoi64 _atoi64 +#define _ttol atol + +// conversion from numbers to strings +#define _i64tot _i64toa +#define _itot _itoa +#define _ltot _ltoa +#define _ui64tot _ui64toa + +// file manipulations +#define _tfopen fopen +#define _topen _open +#define _tremove remove +#define _tunlink _unlink + +// reading and writing to i/o +#define _fgettc fgetc +#define _fgetts fgets +#define _fputts fputs +#define _gettchar getchar + +// directory +#define _tchdir _chdir + +// environment +#define _tgetenv getenv +#define _tsystem system + +// time +#define _tcsftime strftime + +#endif + +// is functions (the same in Unicode / ANSI) +#ifndef _istgraph +#define _istgraph isgraph +#endif +#ifndef _istascii +#define _istascii __isascii +#endif + +#define __TFILE__ _T(__FILE__) +#define __TDATE__ _T(__DATE__) +#define __TTIME__ _T(__TIME__) diff --git a/src/pluginapi.c b/src/pluginapi.c new file mode 100644 index 0000000..1b537da --- /dev/null +++ b/src/pluginapi.c @@ -0,0 +1,298 @@ +#include +#include +#include + +#include "pluginapi.h" + +#ifdef _countof +#define COUNTOF _countof +#else +#define COUNTOF(a) (sizeof(a)/sizeof(a[0])) +#endif + +unsigned int g_stringsize; +stack_t **g_stacktop; +TCHAR *g_variables; + +// utility functions (not required but often useful) + +int NSISCALL popstring(TCHAR *str) +{ + stack_t *th; + if (!g_stacktop || !*g_stacktop) return 1; + th=(*g_stacktop); + + if (str) _tcscpy(str,th->text); + + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + return 0; +} + +int NSISCALL popstringn(TCHAR *str, int maxlen) +{ + stack_t *th; + if (!g_stacktop || !*g_stacktop) return 1; + th=(*g_stacktop); + if (str) lstrcpyn(str,th->text,maxlen?maxlen:g_stringsize); + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + return 0; +} + +void NSISCALL pushstring(const TCHAR *str) +{ + stack_t *th; + if (!g_stacktop) return; + th=(stack_t*)GlobalAlloc(GPTR,(sizeof(stack_t)+(g_stringsize)*sizeof(TCHAR))); + lstrcpyn(th->text,str,g_stringsize); + th->next=*g_stacktop; + *g_stacktop=th; +} + +TCHAR* NSISCALL getuservariable(const int varnum) +{ + if (varnum < 0 || varnum >= __INST_LAST) return NULL; + return g_variables+varnum*g_stringsize; +} + +void NSISCALL setuservariable(const int varnum, const TCHAR *var) +{ + if (var != NULL && varnum >= 0 && varnum < __INST_LAST) + lstrcpy(g_variables + varnum*g_stringsize, var); +} + +#ifdef _UNICODE +int NSISCALL PopStringA(char* ansiStr) +{ + wchar_t* wideStr = (wchar_t*) GlobalAlloc(GPTR, g_stringsize*sizeof(wchar_t)); + int rval = popstring(wideStr); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); + GlobalFree((HGLOBAL)wideStr); + return rval; +} + +int NSISCALL PopStringNA(char* ansiStr, int maxlen) +{ + int realLen = maxlen ? maxlen : g_stringsize; + wchar_t* wideStr = (wchar_t*) GlobalAlloc(GPTR, realLen*sizeof(wchar_t)); + int rval = popstringn(wideStr, realLen); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, realLen, NULL, NULL); + GlobalFree((HGLOBAL)wideStr); + return rval; +} + +void NSISCALL PushStringA(const char* ansiStr) +{ + wchar_t* wideStr = (wchar_t*) GlobalAlloc(GPTR, g_stringsize*sizeof(wchar_t)); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); + pushstring(wideStr); + GlobalFree((HGLOBAL)wideStr); + return; +} + +void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr) +{ + lstrcpyW(wideStr, getuservariable(varnum)); +} + +void NSISCALL GetUserVariableA(const int varnum, char* ansiStr) +{ + wchar_t* wideStr = getuservariable(varnum); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); +} + +void NSISCALL SetUserVariableA(const int varnum, const char* ansiStr) +{ + if (ansiStr != NULL && varnum >= 0 && varnum < __INST_LAST) + { + wchar_t* wideStr = g_variables + varnum * g_stringsize; + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); + } +} + +#else +// ANSI defs +int NSISCALL PopStringW(wchar_t* wideStr) +{ + char* ansiStr = (char*) GlobalAlloc(GPTR, g_stringsize); + int rval = popstring(ansiStr); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); + GlobalFree((HGLOBAL)ansiStr); + return rval; +} + +int NSISCALL PopStringNW(wchar_t* wideStr, int maxlen) +{ + int realLen = maxlen ? maxlen : g_stringsize; + char* ansiStr = (char*) GlobalAlloc(GPTR, realLen); + int rval = popstringn(ansiStr, realLen); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, realLen); + GlobalFree((HGLOBAL)ansiStr); + return rval; +} + +void NSISCALL PushStringW(wchar_t* wideStr) +{ + char* ansiStr = (char*) GlobalAlloc(GPTR, g_stringsize); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); + pushstring(ansiStr); + GlobalFree((HGLOBAL)ansiStr); +} + +void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr) +{ + char* ansiStr = getuservariable(varnum); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); +} + +void NSISCALL GetUserVariableA(const int varnum, char* ansiStr) +{ + lstrcpyA(ansiStr, getuservariable(varnum)); +} + +void NSISCALL SetUserVariableW(const int varnum, const wchar_t* wideStr) +{ + if (wideStr != NULL && varnum >= 0 && varnum < __INST_LAST) + { + char* ansiStr = g_variables + varnum * g_stringsize; + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); + } +} +#endif + +// playing with integers + +INT_PTR NSISCALL nsishelper_str_to_ptr(const TCHAR *s) +{ + INT_PTR v=0; + if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) + { + s++; + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('9')) c-=_T('0'); + else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10; + else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10; + else break; + v<<=4; + v+=c; + } + } + else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) + { + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('7')) c-=_T('0'); + else break; + v<<=3; + v+=c; + } + } + else + { + int sign=0; + if (*s == _T('-')) sign++; else s--; + for (;;) + { + int c=*(++s) - _T('0'); + if (c < 0 || c > 9) break; + v*=10; + v+=c; + } + if (sign) v = -v; + } + + return v; +} + +unsigned int NSISCALL myatou(const TCHAR *s) +{ + unsigned int v=0; + + for (;;) + { + unsigned int c=*s++; + if (c >= _T('0') && c <= _T('9')) c-=_T('0'); + else break; + v*=10; + v+=c; + } + return v; +} + +int NSISCALL myatoi_or(const TCHAR *s) +{ + int v=0; + if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) + { + s++; + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('9')) c-=_T('0'); + else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10; + else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10; + else break; + v<<=4; + v+=c; + } + } + else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) + { + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('7')) c-=_T('0'); + else break; + v<<=3; + v+=c; + } + } + else + { + int sign=0; + if (*s == _T('-')) sign++; else s--; + for (;;) + { + int c=*(++s) - _T('0'); + if (c < 0 || c > 9) break; + v*=10; + v+=c; + } + if (sign) v = -v; + } + + // Support for simple ORed expressions + if (*s == _T('|')) + { + v |= myatoi_or(s+1); + } + + return v; +} + +INT_PTR NSISCALL popintptr() +{ + TCHAR buf[128]; + if (popstringn(buf,COUNTOF(buf))) + return 0; + return nsishelper_str_to_ptr(buf); +} + +int NSISCALL popint_or() +{ + TCHAR buf[128]; + if (popstringn(buf,COUNTOF(buf))) + return 0; + return myatoi_or(buf); +} + +void NSISCALL pushintptr(INT_PTR value) +{ + TCHAR buffer[30]; + wsprintf(buffer, sizeof(void*) > 4 ? _T("%Id") : _T("%d"), value); + pushstring(buffer); +} diff --git a/src/pluginapi.h b/src/pluginapi.h new file mode 100644 index 0000000..ca671a8 --- /dev/null +++ b/src/pluginapi.h @@ -0,0 +1,104 @@ +#ifndef ___NSIS_PLUGIN__H___ +#define ___NSIS_PLUGIN__H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "api.h" +#include "nsis_tchar.h" + +#ifndef NSISCALL +# define NSISCALL __stdcall +#endif + +#define EXDLL_INIT() { \ + g_stringsize=string_size; \ + g_stacktop=stacktop; \ + g_variables=variables; } + +typedef struct _stack_t { + struct _stack_t *next; + TCHAR text[1]; // this should be the length of string_size +} stack_t; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + +extern unsigned int g_stringsize; +extern stack_t **g_stacktop; +extern TCHAR *g_variables; + +void NSISCALL pushstring(const TCHAR *str); +void NSISCALL pushintptr(INT_PTR value); +#define pushint(v) pushintptr((INT_PTR)(v)) +int NSISCALL popstring(TCHAR *str); // 0 on success, 1 on empty stack +int NSISCALL popstringn(TCHAR *str, int maxlen); // with length limit, pass 0 for g_stringsize +INT_PTR NSISCALL popintptr(); +#define popint() ( (int) popintptr() ) +int NSISCALL popint_or(); // with support for or'ing (2|4|8) +INT_PTR NSISCALL nsishelper_str_to_ptr(const TCHAR *s); +#define myatoi(s) ( (int) nsishelper_str_to_ptr(s) ) // converts a string to an integer +unsigned int NSISCALL myatou(const TCHAR *s); // converts a string to an unsigned integer, decimal only +int NSISCALL myatoi_or(const TCHAR *s); // with support for or'ing (2|4|8) +TCHAR* NSISCALL getuservariable(const int varnum); +void NSISCALL setuservariable(const int varnum, const TCHAR *var); + +#ifdef _UNICODE +#define PopStringW(x) popstring(x) +#define PushStringW(x) pushstring(x) +#define SetUserVariableW(x,y) setuservariable(x,y) + +int NSISCALL PopStringA(char* ansiStr); +void NSISCALL PushStringA(const char* ansiStr); +void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr); +void NSISCALL GetUserVariableA(const int varnum, char* ansiStr); +void NSISCALL SetUserVariableA(const int varnum, const char* ansiStr); + +#else +// ANSI defs + +#define PopStringA(x) popstring(x) +#define PushStringA(x) pushstring(x) +#define SetUserVariableA(x,y) setuservariable(x,y) + +int NSISCALL PopStringW(wchar_t* wideStr); +void NSISCALL PushStringW(wchar_t* wideStr); +void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr); +void NSISCALL GetUserVariableA(const int varnum, char* ansiStr); +void NSISCALL SetUserVariableW(const int varnum, const wchar_t* wideStr); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif//!___NSIS_PLUGIN__H___ diff --git a/tools/fix_repo.py b/tools/fix_repo.py new file mode 100644 index 0000000..ec781bb --- /dev/null +++ b/tools/fix_repo.py @@ -0,0 +1,137 @@ +""" +Fix nsSplashPNG repo: +1. Rimuove dal tracking i file di build MSBuild in src/ (artifacts + DLL grezze) +2. Sposta le DLL pre-built in dist/ (struttura canonica) +3. Aggiunge regole .gitignore per escludere le cartelle Release in src/ +4. Aggiunge dist/ al tracking (con DLL binarie) +5. Commit + push Gitea + push GitHub +""" +import os, sys, shutil, subprocess +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent + +# --- Load .env --- +env_path = REPO.parent.parent / '.env' # Launchers/.env +if env_path.exists(): + for line in env_path.read_text(encoding='utf-8').splitlines(): + line = line.strip() + if line and not line.startswith('#') and '=' in line: + k, v = line.split('=', 1) + os.environ.setdefault(k.strip(), v.strip()) + +GITEA_URL = os.environ['GITEA_URL'] +GITEA_USER = os.environ['GITEA_USER'] +GITEA_TOK = os.environ['GITEA_TOKEN'] +GH_USER = os.environ['GITHUB_USER'] +GH_TOK = os.environ['GITHUB_TOKEN'] +REPO_NAME = 'nsis-plugin-nssplashpng' + +def git(*args): + r = subprocess.run(['git', '-C', str(REPO)] + list(args), capture_output=True, text=True) + if r.returncode != 0: + print(f'[git] stderr: {r.stderr.strip()}') + return r + +# 1. Rimuovi dal tracking tutti i file di build in src/ +print('[1] Rimuovo artifacts di build dal tracking...') +tracked_artifacts = [ + 'src/nsSplashPNG/Release Unicode/nsSplashPNG.dll.recipe', + 'src/nsSplashPNG/Release Unicode/nsSplashPNG.iobj', + 'src/nsSplashPNG/Release Unicode/nsSplashPNG.ipdb', + 'src/nsSplashPNG/Release/nsSplashPNG.dll.recipe', + 'src/nsSplashPNG/Release/nsSplashPNG.iobj', + 'src/nsSplashPNG/Release/nsSplashPNG.ipdb', + 'src/nsSplashPNG/x64/Release Unicode/nsSplashPNG.dll.recipe', + 'src/nsSplashPNG/x64/Release Unicode/nsSplashPNG.iobj', + 'src/nsSplashPNG/x64/Release Unicode/nsSplashPNG.ipdb', + 'src/Release Unicode/nsSplashPNG.dll', + 'src/Release/nsSplashPNG.dll', + 'src/x64/Release Unicode/nsSplashPNG.dll', +] +for f in tracked_artifacts: + r = git('rm', '--cached', f) + if r.returncode == 0: + print(f' [rm cached] {f}') + else: + print(f' [skip] {f} — not tracked or already removed') + +# 2. Sposta DLL in dist/ (struttura canonica) +print('[2] Sposto DLL in dist/...') +moves = [ + ('src/Release/nsSplashPNG.dll', 'dist/x86-ansi/nsSplashPNG.dll'), + ('src/Release Unicode/nsSplashPNG.dll', 'dist/x86-unicode/nsSplashPNG.dll'), + ('src/x64/Release Unicode/nsSplashPNG.dll', 'dist/amd64-unicode/nsSplashPNG.dll'), +] +for src_rel, dst_rel in moves: + src = REPO / Path(src_rel) + dst = REPO / Path(dst_rel) + dst.parent.mkdir(parents=True, exist_ok=True) + if src.exists(): + shutil.copy2(src, dst) + print(f' [copied] {src_rel} → {dst_rel}') + elif dst.exists(): + print(f' [exists] {dst_rel} (src già rimosso)') + else: + print(f' [warn] {src_rel} non trovato — DLL mancante') + +# 3. Aggiungi .gitignore rules per le cartelle MSBuild in src/ +print('[3] Aggiorno .gitignore...') +gi_path = REPO / '.gitignore' +gi_content = gi_path.read_text(encoding='utf-8') +extra = ( + '\n# MSBuild output directories in src/ (build artifacts, not source)\n' + 'src/**/Release/\n' + 'src/**/Release Unicode/\n' + 'src/**/x64/\n' + '*.recipe\n' + '*.iobj\n' + '*.ipdb\n' +) +if 'MSBuild output directories' not in gi_content: + gi_path.write_text(gi_content + extra, encoding='utf-8') + print(' [ok] .gitignore aggiornato') +else: + print(' [skip] .gitignore già aggiornato') + +# 4. Stage dist/ e .gitignore +print('[4] Stage dist/ e .gitignore...') +git('add', 'dist/') +git('add', '.gitignore') + +# Verifica status +r = git('status', '--short') +print(r.stdout.strip() or '(nothing staged)') + +# 5. Commit +print('[5] Commit...') +r = git('commit', '-m', + 'fix: move pre-built DLLs to dist/, remove MSBuild artifacts from tracking\n\n' + '- src/Release*/ and src/nsSplashPNG/Release*/ were committed by mistake\n' + ' (MSBuild artifacts: .iobj, .ipdb, .recipe + raw DLL outputs)\n' + '- DLLs moved to canonical dist/{x86-ansi,x86-unicode,amd64-unicode}/\n' + '- .gitignore updated to exclude src/**/Release*/ going forward' +) +if r.returncode != 0: + print(f'[FAIL] commit: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] {r.stdout.strip()}') + +# 6. Push Gitea +print('[6] Push Gitea...') +gitea_url = f"{GITEA_URL.replace('://', f'://{GITEA_USER}:{GITEA_TOK}@')}/{GITEA_USER}/{REPO_NAME}.git" +r = subprocess.run(['git', '-C', str(REPO), 'push', gitea_url, 'main'], + capture_output=True, text=True) +if r.returncode != 0: + print(f'[FAIL] Gitea: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] Gitea: {r.stderr.strip() or "up to date"}') + +# 7. Push GitHub +print('[7] Push GitHub...') +gh_url = f'https://{GH_USER}:{GH_TOK}@github.com/{GH_USER}/{REPO_NAME}.git' +r = subprocess.run(['git', '-C', str(REPO), 'push', gh_url, 'main'], + capture_output=True, text=True) +if r.returncode != 0: + print(f'[FAIL] GitHub: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] GitHub: {r.stderr.strip() or "up to date"}') + +print('[DONE]') diff --git a/tools/push_and_disable_gitea_actions.py b/tools/push_and_disable_gitea_actions.py new file mode 100644 index 0000000..8713069 --- /dev/null +++ b/tools/push_and_disable_gitea_actions.py @@ -0,0 +1,80 @@ +""" +Push latest commit to Gitea + GitHub directly, +then disable Gitea Actions on the repo (keep GitHub Actions enabled). +""" +import urllib.request, urllib.error, json, os, sys, subprocess + +# --- Load .env from workspace root --- +env_path = os.path.join(os.path.dirname(__file__), '..', '..', '..', '.env') +env_path = os.path.normpath(env_path) +if os.path.exists(env_path): + with open(env_path, encoding='utf-8') as f: + for line in f: + line = line.strip() + if line and not line.startswith('#') and '=' in line: + k, v = line.split('=', 1) + os.environ.setdefault(k.strip(), v.strip()) + +GITEA_URL = os.environ['GITEA_URL'] +GITEA_USER = os.environ['GITEA_USER'] +GITEA_TOK = os.environ['GITEA_TOKEN'] +GH_USER = os.environ['GITHUB_USER'] +GH_TOK = os.environ['GITHUB_TOKEN'] +REPO = 'nsis-plugin-nssplashpng' + +STAGING = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) + +def api_call(url, method='GET', data=None, headers=None): + body = json.dumps(data).encode() if data else None + req = urllib.request.Request(url, data=body, headers=headers or {}, method=method) + try: + with urllib.request.urlopen(req) as r: + raw = r.read() + return r.status, json.loads(raw) if raw else {} + except urllib.error.HTTPError as e: + raw = e.read() + return e.code, json.loads(raw) if raw else {} + +# --- 1. Push to Gitea --- +print('[1] Push to Gitea...') +gitea_push_url = f"{GITEA_URL.replace('://', f'://{GITEA_USER}:{GITEA_TOK}@')}/{GITEA_USER}/{REPO}.git" +r = subprocess.run(['git', '-C', STAGING, 'push', gitea_push_url, 'main'], + capture_output=True, text=True) +if r.returncode != 0: + print(f'[FAIL] Gitea push: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] Gitea push: {r.stderr.strip() or "up to date"}') + +# --- 2. Push to GitHub --- +print('[2] Push to GitHub...') +gh_push_url = f'https://{GH_USER}:{GH_TOK}@github.com/{GH_USER}/{REPO}.git' +r = subprocess.run(['git', '-C', STAGING, 'push', gh_push_url, 'main'], + capture_output=True, text=True) +if r.returncode != 0: + print(f'[FAIL] GitHub push: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] GitHub push: {r.stderr.strip() or "up to date"}') + +# --- 3. Disable Gitea Actions --- +print('[3] Disabling Gitea Actions...') +gitea_hdrs = {'Authorization': f'token {GITEA_TOK}', 'Content-Type': 'application/json'} +s, resp = api_call( + f'{GITEA_URL}/api/v1/repos/{GITEA_USER}/{REPO}', + method='PATCH', + data={'has_actions': False}, + headers=gitea_hdrs +) +if s in (200, 201, 204): + actions_status = resp.get('has_actions', '?') + print(f'[ok] Gitea Actions disabled (has_actions={actions_status})') +else: + print(f'[warn] PATCH repo returned {s}: {resp}') + print('[warn] Trying via repo settings API...') + # Alternative: some Gitea versions use a different field name + s2, resp2 = api_call( + f'{GITEA_URL}/api/v1/repos/{GITEA_USER}/{REPO}', + method='PATCH', + data={'actions': False}, + headers=gitea_hdrs + ) + print(f'[info] actions=False attempt: {s2}: {resp2.get("has_actions", resp2.get("actions", "?"))}') + +print('[DONE]') diff --git a/tools/squash_to_single_commit.py b/tools/squash_to_single_commit.py new file mode 100644 index 0000000..d0ec030 --- /dev/null +++ b/tools/squash_to_single_commit.py @@ -0,0 +1,121 @@ +""" +Squash everything in the nsSplashPNG repo into a single initial commit, +then force-push to Gitea and GitHub. + +Steps: +1. Create orphan branch 'init' +2. git add -A (all currently tracked + untracked non-ignored files) +3. Commit with the canonical initial message +4. Replace 'main' with 'init' +5. Delete old v1.0.0 tag, recreate on new commit +6. Force-push main + tag to Gitea and GitHub +""" +import os, sys, subprocess +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent + +# --- Load .env --- +env_path = REPO.parent.parent / '.env' +if env_path.exists(): + for line in env_path.read_text(encoding='utf-8').splitlines(): + line = line.strip() + if line and not line.startswith('#') and '=' in line: + k, v = line.split('=', 1) + os.environ.setdefault(k.strip(), v.strip()) + +GITEA_URL = os.environ['GITEA_URL'] +GITEA_USER = os.environ['GITEA_USER'] +GITEA_TOK = os.environ['GITEA_TOKEN'] +GH_USER = os.environ['GITHUB_USER'] +GH_TOK = os.environ['GITHUB_TOKEN'] +REPO_NAME = 'nsis-plugin-nssplashpng' + +GITEA_PUSH = f"{GITEA_URL.replace('://', f'://{GITEA_USER}:{GITEA_TOK}@')}/{GITEA_USER}/{REPO_NAME}.git" +GH_PUSH = f'https://{GH_USER}:{GH_TOK}@github.com/{GH_USER}/{REPO_NAME}.git' + +COMMIT_MSG = 'chore: initial commit (extracted from Launchers monorepo)' +TAG = 'v1.0.0' + +def git(*args, check=True): + r = subprocess.run(['git', '-C', str(REPO)] + list(args), + capture_output=True, text=True) + if check and r.returncode != 0: + print(f'[git {" ".join(args)}] stderr: {r.stderr.strip()}', file=sys.stderr) + sys.exit(1) + return r + +# 1. Crea branch orfano +print('[1] Creo branch orfano "init"...') +git('checkout', '--orphan', 'init') + +# 2. Stage tutto (include file attualmente tracciati + non-ignorati) +print('[2] Stage di tutti i file...') +git('add', '-A') + +# Verifica cosa verrà committato +r = git('status', '--short') +print(r.stdout.strip()) + +# 3. Commit +print('[3] Commit unico...') +# Configura autore se non già globale +r = git('commit', '-m', COMMIT_MSG) +print(f'[ok] {r.stdout.strip()}') + +# 4. Sostituisci main con init +print('[4] Sostituisco branch main...') +git('branch', '-D', 'main') +git('branch', '-m', 'init', 'main') + +# 5. Elimina vecchio tag e ricrealo sul nuovo commit +print(f'[5] Ricreo tag {TAG}...') +git('tag', '-d', TAG, check=False) # potrebbe non esistere +git('tag', TAG) + +# Verifica finale +r = git('log', '--oneline') +print(f'[log] {r.stdout.strip()}') +r = git('tag', '--list') +print(f'[tags] {r.stdout.strip()}') + +# 6. Force push a Gitea +print('[6] Force push a Gitea...') +r = subprocess.run( + ['git', '-C', str(REPO), 'push', '--force', GITEA_PUSH, 'main', TAG], + capture_output=True, text=True +) +if r.returncode != 0: + print(f'[FAIL] Gitea: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] Gitea: {r.stderr.strip()}') + +# 7. Force push a GitHub +print('[7] Force push a GitHub...') +r = subprocess.run( + ['git', '-C', str(REPO), 'push', '--force', GH_PUSH, 'main', TAG], + capture_output=True, text=True +) +if r.returncode != 0: + print(f'[FAIL] GitHub: {r.stderr}', file=sys.stderr); sys.exit(1) +print(f'[ok] GitHub: {r.stderr.strip()}') + +# 8. Aggiorna origin/main +print('[8] Aggiorno remote tracking...') +r = subprocess.run( + ['git', '-C', str(REPO), 'remote', 'set-head', 'origin', 'main'], + capture_output=True, text=True +) +# Fetch per sincronizzare origin/main con il nuovo HEAD +gitea_fetch = GITEA_PUSH +r2 = subprocess.run( + ['git', '-C', str(REPO), 'fetch', '--force', gitea_fetch, 'main:refs/remotes/origin/main'], + capture_output=True, text=True +) + +# Verifica stato finale +r = git('log', '--oneline', '-3') +print(f'\n[FINAL LOG]\n{r.stdout.strip()}') +r = git('status', '-sb') +print(f'[FINAL STATUS]\n{r.stdout.strip()}') + +print('\n[DONE] Repository ridotto a singolo commit iniziale.')