Files
2026-04-30 00:13:24 +02:00

289 lines
9.1 KiB
Markdown

# nsParser NSIS Plugin
Plugin NSIS nativo in C per estrarre velocemente valori da file di grandi dimensioni, bypassando i limiti di NSIS_MAX_STRLEN (1KB).
## Problema Risolto
NSIS ha un limite di **1024 byte** per le stringhe (`NSIS_MAX_STRLEN`). File HTML/JSON con linee molto lunghe (>1KB) non possono essere processati con `FileRead` o operazioni su stringhe native NSIS.
**Esempio**: Il file HTML di Remote Desktop Manager contiene una linea di 39.830 caratteri (135KB totali) che causa il troncamento di `FileRead` a 1KB, impedendo l'estrazione della versione.
**Soluzioni fallite**:
- `FileRead` → Tronca a 1KB
- Concatenazione stringhe → Limite 1023 byte totali
- `NScurl /MEMORY` → Limite ~8KB
- Lettura byte-by-byte → Troppo lenta (10-30 secondi per 135KB)
**Soluzione nsParser**: Plugin nativo in C che legge file in chunk di 8KB con buffer sliding window di 16KB, parsing in <1 secondo.
## Compilazione
### Requisiti
- Visual Studio 2022 (MSBuild 17.x, PlatformToolset v143)
- Python 3.x (per build automation)
### Build Automatica
```cmd
cd nsParser
python build_plugin.py
```
Compila tutte le configurazioni e copia i DLL finali in `../plugins/`:
- `x86-ansi` → 85-87 KB
- `x86-unicode` → 85-87 KB
- `x64-ansi` → 104 KB
- `amd64-unicode` → 104 KB
File intermedi (`.lib`, `.exp`, `.obj`) rimangono in `nsParser/Plugins/{platform}/` per debugging.
### Opzioni build
```powershell
python build_plugin.py --config x86-unicode # Solo un'architettura (x86-ansi|x86-unicode|amd64-unicode|all)
python build_plugin.py --toolset 2026 # Toolset specifico (2022|2026|auto)
python build_plugin.py --jobs 4 # Numero di job MSBuild paralleli (default: CPU count)
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 --verbose # Output MSBuild esteso
python build_plugin.py --version # Stampa versione ed esce
```
### Build Manuale
```cmd
cd nsParser
build.cmd
```
O con MSBuild direttamente:
```cmd
msbuild nsParser.vcxproj /p:Configuration="Release" /p:Platform="Win32"
msbuild nsParser.vcxproj /p:Configuration="Release Unicode" /p:Platform="Win32"
msbuild nsParser.vcxproj /p:Configuration="Release" /p:Platform="x64"
msbuild nsParser.vcxproj /p:Configuration="Release Unicode" /p:Platform="x64"
```
## Utilizzo in NSIS
```nsis
!addplugindir "plugins\x86-unicode"
Section
; Download HTML file
NScurl::http GET "https://devolutions.net/..." "$PLUGINSDIR\rdm.html" /END
Pop $0
; Extract version from HTML
nsParser::Extract "$PLUGINSDIR\rdm.html" "RDM7zX64.Version"
Pop $0 ; Status: "OK" or "ERROR"
Pop $1 ; Version string (es: "2025.3.21.0")
${If} $0 == "OK"
DetailPrint "Version found: $1"
${Else}
DetailPrint "Version not found"
${EndIf}
SectionEnd
```
### Esempio Reale: RDM_Functions.nsh
```nsis
!macro _GetLatestVersion
NScurl::http GET "https://devolutions.net/..." "$R8" /END
Pop $0
${If} $0 == "OK"
nsParser::Extract "$R8" "RDM7zX64.Version"
Pop $0 ; Status
Pop $1 ; Version: "2025.3.21.0"
${If} $0 == "OK"
StrCpy $Latest_Version "$1"
StrCpy $DOWNLOAD_LINK "https://cdn.devolutions.net/download/Devolutions.RemoteDesktopManager.win-x64.$Latest_Version.7z"
${EndIf}
${EndIf}
!macroend
```
## API
### nsParser::Extract
```nsis
nsParser::Extract <filepath> <pattern>
```
**Parametri**:
- `<filepath>`: Percorso assoluto del file da analizzare
- `<pattern>`: Pattern da cercare (es: "RDM7zX64.Version")
**Stack Output**:
1. Status: `"OK"` o `"ERROR"`
2. Value: Stringa estratta (o vuota se errore)
**Logica di estrazione**:
1. Cerca `<pattern>` nel file
2. Trova il primo `:` dopo il pattern
3. Trova il primo `"` dopo i due punti
4. Estrae tutto fino al `"` di chiusura
5. Restituisce il valore trovato
**Esempio**: Nel file con contenuto `{"RDM7zX64.Version":"2025.3.21.0"}`, il pattern `"RDM7zX64.Version"` estrae `"2025.3.21.0"`.
## Vantaggi
- **Veloce**: ~1000x più veloce del byte-reading NSIS (<1s vs 10-30s per 135KB)
- **No Limiti**: Bypassa NSIS_MAX_STRLEN, gestisce file di qualsiasi dimensione
- **Efficiente**: Lettura in chunk di 8KB con sliding window buffer di 16KB
- **Pattern Spanning**: Buffer sliding window preserva pattern tra chunk (ultimi 2KB)
- **Multi-Arch**: 4 configurazioni (x86/x64, ANSI/Unicode)
- **Semplice**: Singola funzione `Extract` parametrica
- **Generico**: Non specifico per RDM, usabile per qualsiasi estrazione pattern
## Implementazione Tecnica
### Algoritmo
```c
1. CreateFile() Apre file in lettura
2. Loop:
a. ReadFile() Legge 8KB chunk
b. Append a search buffer (16KB max)
c. strstr() Cerca pattern
d. Se trovato:
- Trova ':' dopo pattern
- Trova '"' dopo ':'
- Estrae fino a '"' chiusura
- Return value
e. Se buffer pieno:
- Copia ultimi 2KB all'inizio (sliding window)
- Azzera resto buffer
- Continue reading
3. CloseHandle()
```
### Caratteristiche
- **Buffer Size**: 16KB totale (8KB chunk + 8KB overlap)
- **Sliding Window**: Preserva ultimi 2KB per pattern spanning
- **String Search**: Native `strstr()` su dati ANSI
- **Unicode Support**: `MultiByteToWideChar()` per output Unicode
- **Windows API**: `CreateFile`, `ReadFile`, `CloseHandle`
- **No CRT**: Runtime library statica (MultiThreaded)
### Configurazioni Visual Studio
| Configuration | Platform | CharacterSet | Output Size | Plugin Dir |
|---------------|----------|--------------|-------------|------------|
| Release | Win32 | MultiByte | 85-87 KB | x86-ansi |
| Release Unicode | Win32 | Unicode | 85-87 KB | x86-unicode |
| Release | x64 | MultiByte | 104 KB | x64-ansi |
| Release Unicode | x64 | Unicode | 104 KB | amd64-unicode |
### Struttura File
```
nsParser/
├── nsParser.c # Implementazione plugin (140 righe)
├── nsParser.h # Header NSISFUNC macro
├── nsParser.def # Export definitions
├── nsParser.vcxproj # Visual Studio project (4 configs)
├── build_plugin.py # Build automation + DLL copy
├── build.cmd # Batch wrapper
├── .gitignore # Ignora Plugins/*, .user, __pycache__
├── README.md # Questa documentazione
└── Plugins/ # Build intermedi (gitignored)
├── x86-ansi/ # DLL + .lib + .exp + .obj
├── x86-unicode/
├── x64-ansi/
└── amd64-unicode/
../plugins/ # Output finale (copiato da Python)
├── x86-ansi/nsParser.dll
├── x86-unicode/nsParser.dll
├── x64-ansi/nsParser.dll
└── amd64-unicode/nsParser.dll
```
## Note Tecniche
### NSIS_MAX_STRLEN Limit
Il limite di 1KB è **assoluto** in NSIS Unicode e non può essere aumentato senza ricompilare NSIS stesso. Questo plugin è l'unica soluzione pratica per processare file con linee >1KB.
### VersionCompare Bug
**ATTENZIONE**: `${VersionCompare}` ha un bug noto con versioni a 4 parti. Esempio:
```nsis
${VersionCompare} "2024.3.18.0" "2025.3.21.0" $R0
; $R0 = "2" (SBAGLIATO! Dovrebbe essere "1")
```
**Workaround** (usato in `RDM_Installer.nsi`):
```nsis
${VersionCompare} "$Current_Version" "$Latest_Version" $R0
${If} $R0 == "2"
; Estrai anno e confronta numericamente
${WordFind} "$Current_Version" "." "+1{" $R1 ; Anno corrente
${WordFind} "$Latest_Version" "." "+1{" $R2 ; Anno remoto
${If} $R1 < $R2
; Anno vecchio → forzare selezione
!insertmacro SelectSection 0
${Else}
!insertmacro UnselectSection 0
${EndIf}
${EndIf}
```
**Nota**: In `RDM.nsi` (launcher) il workaround non serve perché si verifica solo uguaglianza (`$R0 == "0"`), non maggiore/minore.
## Performance
### Benchmark: File HTML 135KB (linea 39.830 caratteri)
| Metodo | Tempo | Note |
|--------|-------|------|
| NSIS FileRead | ❌ Fallisce | Tronca a 1KB |
| NSIS Byte-reading | 10-30 secondi | Troppo lento |
| NScurl /MEMORY | ❌ Fallisce | Limite ~8KB |
| **nsParser plugin** | **<1 secondo** | ✅ Soluzione ottimale |
### Perché è così veloce?
1. **Native C**: Compiled code vs NSIS interpreted
2. **Bulk Reading**: 8KB chunks vs 1-byte iterations
3. **Efficient Search**: `strstr()` CPU-optimized vs NSIS string ops
4. **No String Limits**: Direct memory access vs NSIS_MAX_STRLEN
## Casi d'Uso
- ✅ Estrazione versioni da HTML/JSON con linee lunghe
- ✅ Parsing file di configurazione con valori embedded
- ✅ Lettura metadata da file generati (build info, manifests)
- ✅ Download + parsing di risorse remote in un unico passaggio
- ✅ Qualsiasi pattern `"key":"value"` o `key: "value"`
## Licenza
Sviluppato per uso interno nel workspace Launchers.
## Changelog
### v1.0 (2025-11-10)
- ✅ Rinominato da RDMVersion a nsParser (generico)
- ✅ Supporto multi-architettura (4 configurazioni)
- ✅ Build automation con Python
- ✅ Struttura directory pulita (Plugins/ + ../plugins/)
- ✅ Fixed amd64-unicode configuration bug
- ✅ Integrato in RDM_Installer.nsi e RDM_Functions.nsh
- ✅ Performance validated: <1s per 135KB file
### v0.1 (Initial)
- Plugin iniziale RDMVersion specifico per Remote Desktop Manager