2 Commits

Author SHA1 Message Date
Simone 4b12e21dd4 chore: release v2.1.0 2026-04-30 22:25:32 +02:00
Simone e402720a12 Add NSIS plugin support and extraction functionality
- Implemented plugin API in `pluginapi.cpp` and `pluginapi.h` for NSIS integration.
- Added resource files `resource.h` and `resource.rc` for versioning and resource management.
- Created `ExtractCallbackConsole` class to handle extraction progress and user input.
- Developed main extraction logic in `Main.cpp` and `MainAr.cpp` for handling archives.
- Introduced user input utilities in `UserInputUtils2.cpp` and `UserInputUtils2.h` for password handling and user prompts.
- Added break signal handling in `NSISBreak.cpp` and `NSISBreak.h` for graceful interruption of extraction processes.
- Included standard header `StdAfx.h` for precompiled headers and common includes.
2026-04-30 17:27:38 +02:00
38 changed files with 3739 additions and 20 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "versions/7-zip-zstd"]
path = versions/7-zip-zstd
url = https://github.com/mcmilk/7-Zip-zstd.git
+15 -1
View File
@@ -7,6 +7,19 @@ e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.h
## [Unreleased]
## [2.1.0] — 2026-04-30
### Added
- NSIS plugin API (`pluginapi.cpp`/`pluginapi.h`) for 7-Zip zstd build
- `ExtractCallbackConsole` class for extraction progress and user input
- Main extraction logic (`Main.cpp`, `MainAr.cpp`) for the zstd bundle
- User input utilities (`UserInputUtils2`) for password handling and prompts
- Break signal handling (`NSISBreak`) for graceful interruption
- `Nsis7z_vs2026.vcxproj` (v145 toolset) for Visual Studio 2026 builds
- `tools/fix_vcxproj.py` helper for project file patching
- `versions/7-zip-zstd` submodule added
- `build_zstd.cmd` top-level build script for the zstd variant
## [2.0.1] — 2026-04-29
### Fixed
@@ -23,6 +36,7 @@ e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.h
- CI/CD via GitHub Actions (mirror automatico Gitea → GitHub)
- Documentazione completa (README, CONTRIBUTING, SECURITY)
[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.0.1...HEAD
[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.1.0...HEAD
[2.1.0]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.0.1...v2.1.0
[2.0.1]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.0.0...v2.0.1
[2.0.0]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/releases/tag/v2.0.0
+1 -1
View File
@@ -1 +1 @@
2.0.1
2.1.0
+7
View File
@@ -0,0 +1,7 @@
@echo off
:: Generic wrapper for build_plugin.py
:: Usage: build.cmd [--7zip-version 19.00|25.01|26.00|zstd] [--toolset 2022|2026|auto] [...]
:: Default: --7zip-version 26.00 --toolset auto
setlocal
python "%~dp0build_plugin.py" %*
exit /b %errorlevel%
+6
View File
@@ -0,0 +1,6 @@
@echo off
:: Build ns7zip using 7-Zip 19.00 sources
:: Usage: build_1900.cmd [--toolset 2022|2026|auto] [...]
setlocal
python "%~dp0build_plugin.py" --7zip-version 19.00 %*
exit /b %errorlevel%
+6
View File
@@ -0,0 +1,6 @@
@echo off
:: Build ns7zip using 7-Zip 25.01 sources
:: Usage: build_2501.cmd [--toolset 2022|2026|auto] [...]
setlocal
python "%~dp0build_plugin.py" --7zip-version 25.01 %*
exit /b %errorlevel%
+6
View File
@@ -0,0 +1,6 @@
@echo off
:: Build ns7zip using 7-Zip 26.00 sources (default)
:: Usage: build_2600.cmd [--toolset 2022|2026|auto] [...]
setlocal
python "%~dp0build_plugin.py" --7zip-version 26.00 %*
exit /b %errorlevel%
+7 -3
View File
@@ -23,14 +23,17 @@ SCRIPTS = {
"2026": "tools/legacy/build_plugin_2501_vs2026.py"},
"26.00": {"2022": None,
"2026": "tools/legacy/build_plugin_2600_vs2026.py"},
"zstd": {"2022": None,
"2026": "tools/legacy/build_plugin_zstd_vs2026.py"},
}
def main() -> int:
parser = argparse.ArgumentParser(description="Build nsis7z NSIS plugin")
parser.add_argument("--7zip-version", dest="zip_version",
choices=["19.00", "25.01", "26.00"], default="26.00",
help="7-Zip version to build (default: 26.00)")
choices=["19.00", "25.01", "26.00", "zstd"], default="26.00",
help="7-Zip version to build (default: 26.00); "
"'zstd' uses mcmilk/7-Zip-zstd submodule")
parser.add_argument("--toolset", choices=["2022", "2026", "auto"], default="auto",
help="Visual Studio toolset version (default: auto)")
parser.add_argument("--version", action="store_true",
@@ -42,8 +45,9 @@ def main() -> int:
return 0
ver = (ROOT / "VERSION").read_text(encoding="utf-8-sig").strip()
zip_label = "7z-ZS" if known.zip_version == "zstd" else f"7z {known.zip_version}"
print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}=== Building ns7zip v{ver} "
f"(7z {known.zip_version}) ==={Colors.RESET}")
f"({zip_label}) ==={Colors.RESET}")
toolset = known.toolset
if toolset == "auto":
+6
View File
@@ -0,0 +1,6 @@
@echo off
:: Build ns7zip using 7-Zip-zstd sources (mcmilk fork)
:: Usage: build_zstd.cmd [--toolset 2022|2026|auto] [...]
setlocal
python "%~dp0build_plugin.py" --7zip-version zstd %*
exit /b %errorlevel%
Binary file not shown.
Binary file not shown.
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Add missing source files to Nsis7z_vs2026.vcxproj"""
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
vcxproj = ROOT / 'versions/zstd/CPP/7zip/Bundles/Nsis7z/Nsis7z_vs2026.vcxproj'
# Relative paths from vcxproj dir (versions/zstd/CPP/7zip/Bundles/Nsis7z/)
# 5 levels up = versions/
MISSING = [
r'..\..\..\..\..\7-zip-zstd\CPP\Common\MyWindows.cpp',
r'..\..\..\..\..\7-zip-zstd\C\hashes\xxhash.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\dict_buffer.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\fl2_common.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\fl2_compress.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\fl2_pool.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\fl2_threading.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\lzma2_enc.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\radix_bitpack.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\radix_mf.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\radix_struct.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\range_enc.c',
r'..\..\..\..\..\7-zip-zstd\C\fast-lzma2\util.c',
]
content = vcxproj.read_text(encoding='utf-8')
ANCHOR = ' <ClCompile Include="..\\..\\UI\\NSIS\\ExtractCallbackConsole.cpp" />'
if ANCHOR not in content:
print('ERROR: anchor not found in vcxproj')
exit(1)
lines = [f' <ClCompile Include="{p}" />' for p in MISSING]
block = '\n'.join(lines) + '\n '
content = content.replace(ANCHOR, block + ANCHOR.lstrip())
vcxproj.write_text(content, encoding='utf-8')
print(f'Added {len(MISSING)} source files to vcxproj')
+14 -1
View File
@@ -84,6 +84,7 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
result = subprocess.run(
[
str(_VSWHERE_PATH),
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-requires', 'Microsoft.Component.MSBuild',
@@ -102,6 +103,13 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
return None
# Numeric version folder used by VS2026 installer (version 18.x)
_VS_NUMERIC_FOLDER = {
'2026': '18',
'2022': '2022',
}
def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
"""Find MSBuild.exe via vswhere, then well-known fallback paths.
@@ -114,7 +122,12 @@ def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
vs_editions = ['Community', 'Professional', 'Enterprise', 'BuildTools']
versions_to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
for version in versions_to_try:
base_path = Path(rf'C:\Program Files\Microsoft Visual Studio\{version}')
numeric = _VS_NUMERIC_FOLDER.get(version, version)
base_paths = [
Path(rf'C:\Program Files (x86)\Microsoft Visual Studio\{numeric}'),
Path(rf'C:\Program Files\Microsoft Visual Studio\{version}'),
]
for base_path in base_paths:
for edition in vs_editions:
msbuild_path = base_path / edition / 'MSBuild' / 'Current' / 'Bin' / 'MSBuild.exe'
if msbuild_path.exists():
+14 -1
View File
@@ -84,6 +84,7 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
result = subprocess.run(
[
str(_VSWHERE_PATH),
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-requires', 'Microsoft.Component.MSBuild',
@@ -102,6 +103,13 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
return None
# Numeric version folder used by VS2026 installer (version 18.x)
_VS_NUMERIC_FOLDER = {
'2026': '18',
'2022': '2022',
}
def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
"""Find MSBuild.exe via vswhere, then well-known fallback paths.
@@ -114,7 +122,12 @@ def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
vs_editions = ['Community', 'Professional', 'Enterprise', 'BuildTools']
versions_to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
for version in versions_to_try:
base_path = Path(rf'C:\Program Files\Microsoft Visual Studio\{version}')
numeric = _VS_NUMERIC_FOLDER.get(version, version)
base_paths = [
Path(rf'C:\Program Files (x86)\Microsoft Visual Studio\{numeric}'),
Path(rf'C:\Program Files\Microsoft Visual Studio\{version}'),
]
for base_path in base_paths:
for edition in vs_editions:
msbuild_path = base_path / edition / 'MSBuild' / 'Current' / 'Bin' / 'MSBuild.exe'
if msbuild_path.exists():
+14 -1
View File
@@ -84,6 +84,7 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
result = subprocess.run(
[
str(_VSWHERE_PATH),
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-requires', 'Microsoft.Component.MSBuild',
@@ -102,6 +103,13 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
return None
# Numeric version folder used by VS2026 installer (version 18.x)
_VS_NUMERIC_FOLDER = {
'2026': '18',
'2022': '2022',
}
def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
"""Find MSBuild.exe via vswhere, then well-known fallback paths.
@@ -114,7 +122,12 @@ def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
vs_editions = ['Community', 'Professional', 'Enterprise', 'BuildTools']
versions_to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
for version in versions_to_try:
base_path = Path(rf'C:\Program Files\Microsoft Visual Studio\{version}')
numeric = _VS_NUMERIC_FOLDER.get(version, version)
base_paths = [
Path(rf'C:\Program Files (x86)\Microsoft Visual Studio\{numeric}'),
Path(rf'C:\Program Files\Microsoft Visual Studio\{version}'),
]
for base_path in base_paths:
for edition in vs_editions:
msbuild_path = base_path / edition / 'MSBuild' / 'Current' / 'Bin' / 'MSBuild.exe'
if msbuild_path.exists():
+700
View File
@@ -0,0 +1,700 @@
#!/usr/bin/env python3
"""
Build script for nsis7z plugin (7-Zip ZS / mcmilk/7-Zip-zstd) - Visual Studio 2026
Supports multiple build configurations with flexible parameters.
Uses PlatformToolset v145 (Visual Studio 2026 Build Tools).
The 7-Zip ZS source is pulled from the git submodule at versions/7-zip-zstd/.
The NSIS plugin wrapper (nsis7z.cpp, vcxproj, …) lives in versions/zstd/.
NOTE: VS2026 Build Tools use v145 toolset (version 18.x)
"""
import argparse
import os
import sys
import subprocess
import shutil
import time
import multiprocessing
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import List, Tuple, Optional
import xml.etree.ElementTree as ET
class BuildConfig:
"""Configuration for a single build target"""
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
# Predefined build configurations
CONFIGS = {
'x86-unicode': BuildConfig(
name='x86-unicode',
config='Release Unicode',
platform='Win32',
dest_dir='x86-unicode'
),
'x64-unicode': BuildConfig(
name='x64-unicode',
config='Release Unicode',
platform='x64',
dest_dir='amd64-unicode'
),
'x86-ansi': BuildConfig(
name='x86-ansi',
config='Release',
platform='Win32',
dest_dir='x86-ansi'
),
}
# Standard vswhere.exe location (installed with any VS 2017+ setup)
_VSWHERE_PATH = Path(r'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')
# vswhere version range per VS year
_VS_VERSION_RANGE = {
'2026': '[18.0,19.0)',
'2022': '[17.0,18.0)',
}
# MSBuild toolset per VS year
_VS_TOOLSET = {
'2026': 'v145', # VS 2026 uses v145 toolset
'2022': 'v143', # VS 2022 uses v143 toolset
}
def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str]]':
"""Locate MSBuild via vswhere.exe."""
if not _VSWHERE_PATH.exists():
return None
versions_to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
for ver in versions_to_try:
if ver not in _VS_VERSION_RANGE:
continue
try:
result = subprocess.run(
[
str(_VSWHERE_PATH),
'-products', '*',
'-version', _VS_VERSION_RANGE[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:
msbuild_path = Path(lines[0])
if msbuild_path.exists():
return msbuild_path, _VS_TOOLSET[ver], ver
except Exception:
pass
return None
# Numeric version folder used by VS2026 installer (version 18.x)
_VS_NUMERIC_FOLDER = {
'2026': '18',
'2022': '2022',
}
def find_msbuild(vs_version: str = 'auto') -> 'Optional[Tuple[Path, str, str]]':
"""Find MSBuild.exe via vswhere, then well-known fallback paths.
Returns (msbuild_path, toolset_version, vs_year) or None.
"""
result = _find_msbuild_via_vswhere(vs_version)
if result is not None:
return result
vs_editions = ['Community', 'Professional', 'Enterprise', 'BuildTools']
versions_to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
for version in versions_to_try:
numeric = _VS_NUMERIC_FOLDER.get(version, version)
base_paths = [
Path(rf'C:\Program Files (x86)\Microsoft Visual Studio\{numeric}'),
Path(rf'C:\Program Files\Microsoft Visual Studio\{version}'),
]
for base_path in base_paths:
for edition in vs_editions:
msbuild_path = base_path / edition / 'MSBuild' / 'Current' / 'Bin' / 'MSBuild.exe'
if msbuild_path.exists():
return msbuild_path, _VS_TOOLSET[version], version
return None
def get_project_paths(toolset: str = 'v145') -> Tuple[Path, Path, Path]:
"""Get project directory, project file, and plugins directory"""
script_dir = Path(__file__).parent.absolute()
project_dir = script_dir.parent.parent / 'versions' / 'zstd'
vcxproj = 'Nsis7z_vs2026.vcxproj'
project_file = project_dir / 'CPP' / '7zip' / 'Bundles' / 'Nsis7z' / vcxproj
plugins_dir = script_dir.parent.parent / 'plugins'
return project_dir, project_file, plugins_dir
def get_cpu_info() -> dict:
"""Get CPU information for parallel build optimization"""
try:
import psutil
logical_cores = psutil.cpu_count(logical=True)
physical_cores = psutil.cpu_count(logical=False)
return {
'logical_cores': logical_cores,
'physical_cores': physical_cores,
'has_hyperthreading': logical_cores > physical_cores,
'has_psutil': True
}
except ImportError:
cpu_count = multiprocessing.cpu_count()
return {
'logical_cores': cpu_count,
'physical_cores': cpu_count,
'has_hyperthreading': False,
'has_psutil': False
}
def get_optimal_thread_count() -> int:
"""Get optimal thread count for compilation"""
cpu_info = get_cpu_info()
if cpu_info['has_hyperthreading'] and cpu_info['physical_cores'] > 1:
return cpu_info['physical_cores']
return cpu_info['logical_cores']
def get_build_optimizations() -> List[str]:
"""Get additional build optimization flags"""
return [
'/p:BuildInParallel=true',
'/p:MultiProcessorCompilation=true',
'/p:PreferredToolArchitecture=x64',
'/p:UseSharedCompilation=true',
'/p:TrackFileAccess=false',
'/nodeReuse:true',
'/p:GenerateResourceUsePreserializedResources=true'
]
def get_memory_optimizations() -> List[str]:
"""Get memory optimization flags for faster builds"""
try:
import psutil
memory_gb = psutil.virtual_memory().total / (1024 ** 3)
if memory_gb >= 16:
return [
'/p:DisableFastUpToDateCheck=false',
'/p:BuildProjectReferences=true',
'/p:UseCommonOutputDirectory=false'
]
elif memory_gb >= 8:
return ['/p:DisableFastUpToDateCheck=false']
else:
return []
except ImportError:
return ['/p:DisableFastUpToDateCheck=false']
def print_cpu_info(use_parallel: bool, use_optimizations: bool = True) -> None:
"""Print CPU information for build optimization"""
if not use_parallel:
print("Build mode: Single-threaded")
if use_optimizations:
print("Optimizations: ENABLED (memory, caching)")
return
cpu_info = get_cpu_info()
optimal_threads = get_optimal_thread_count()
print("Build mode: Parallel")
print(f"Logical cores: {cpu_info['logical_cores']}")
print(f"Physical cores: {cpu_info['physical_cores']}")
if cpu_info['has_hyperthreading']:
print("Hyperthreading: ENABLED")
print(f"Optimal threads: {optimal_threads} (using physical cores)")
else:
print("Hyperthreading: NOT AVAILABLE")
print(f"Optimal threads: {optimal_threads}")
print(f"MSBuild threads: {optimal_threads}")
if use_optimizations:
print("Optimizations: ENABLED (parallel, memory, caching)")
try:
import psutil
memory_gb = psutil.virtual_memory().total / (1024 ** 3)
print(f"Available memory: {memory_gb:.1f} GB")
except ImportError:
print("Available memory: Unknown (install psutil for details)")
else:
print("Optimizations: DISABLED")
if not cpu_info['has_psutil']:
print("Note: Install 'psutil' for detailed CPU/memory info (pip install psutil)")
print()
def build_configuration(
msbuild_path: Path,
project_file: Path,
config: BuildConfig,
rebuild: bool = True,
verbosity: str = 'quiet',
parallel: bool = True,
optimizations: bool = True,
counter: str = "",
capture_output: bool = False,
toolset: str = 'v145',
) -> Tuple[bool, float, str]:
"""Build a single configuration.
Returns (success, elapsed_time, captured_output).
"""
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={toolset}',
f'/v:{verbosity}',
]
if parallel:
optimal_threads = get_optimal_thread_count()
cmd.extend([
f'/maxcpucount:{optimal_threads}',
'/p:UseMultiToolTask=true',
f'/p:CL_MPCount={optimal_threads}'
])
if optimizations:
cmd.extend(get_build_optimizations())
cmd.extend(get_memory_optimizations())
cache_dir = setup_build_cache()
if cache_dir:
cmd.append('/p:MSBuildCacheEnabled=true')
if not capture_output:
print(f"\n{'=' * 50}")
if counter:
print(f"Building {config.name} [{counter}]")
else:
print(f"Building {config.name}...")
print('=' * 50)
start_time = time.time()
try:
if capture_output:
result = subprocess.run(cmd, check=False, capture_output=True, text=True)
elapsed = time.time() - start_time
return result.returncode == 0, elapsed, result.stdout + result.stderr
else:
result = subprocess.run(cmd, check=False)
elapsed = time.time() - start_time
return result.returncode == 0, elapsed, ""
except Exception as e:
elapsed = time.time() - start_time
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,
plugins_dir: Path,
config: BuildConfig
) -> Tuple[bool, int, Optional[Path]]:
"""Copy built DLL to destination directory.
Returns (success, file_size, dest_path).
"""
output_file = (
project_dir / 'CPP' / '7zip' / 'Bundles' / 'Nsis7z'
/ 'Build' / config.name / 'nsis7z.dll'
)
dest_dir = plugins_dir / config.dest_dir
if not output_file.exists():
print(f"ERROR: {config.name} DLL not found at {output_file}")
return False, 0, None
dest_dir.mkdir(parents=True, exist_ok=True)
try:
dest_path = dest_dir / 'nsis7z.dll'
shutil.copy2(output_file, dest_path)
file_size = output_file.stat().st_size
print(f"SUCCESS: {config.name} - {file_size:,} bytes - Copied to {dest_dir}")
return True, file_size, dest_path
except Exception as e:
print(f"ERROR: Failed to copy {config.name}: {e}")
return False, 0, None
def clean_build_artifacts(project_dir: Path, configs: List[BuildConfig]) -> None:
"""Clean up build artifacts"""
print("\nCleaning build artifacts...")
build_base_dir = (
project_dir / 'CPP' / '7zip' / 'Bundles' / 'Nsis7z' / 'Build'
)
dirs_to_clean = {build_base_dir / config.name for config in configs}
for dir_path in dirs_to_clean:
if dir_path.exists():
try:
shutil.rmtree(dir_path)
print(f" - Cleaned: {dir_path.name}")
except Exception as e:
print(f" - Failed to clean {dir_path.name}: {e}")
print("Build artifacts cleaned successfully.")
def format_time(seconds: float) -> str:
"""Format seconds to human-readable string"""
if seconds < 60:
return f"{seconds:.2f}s"
elif seconds < 3600:
minutes = int(seconds // 60)
secs = seconds % 60
return f"{minutes}m {secs:.1f}s"
else:
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = seconds % 60
return f"{hours}h {minutes}m {secs:.0f}s"
def setup_build_cache() -> Optional[str]:
"""Setup build cache directory for faster incremental builds"""
try:
cache_dir = Path.home() / '.msbuild_cache' / 'nsis7z-zstd-vs2026'
cache_dir.mkdir(parents=True, exist_ok=True)
return str(cache_dir)
except Exception:
return None
def get_available_configurations(project_file: Path) -> List[Tuple[str, str]]:
"""Parse .vcxproj file to get available configurations"""
try:
tree = ET.parse(project_file)
root = tree.getroot()
ns = {'ms': 'http://schemas.microsoft.com/developer/msbuild/2003'}
configs = []
for item_group in root.findall('.//ms:ItemGroup', ns):
for proj_config in item_group.findall('ms:ProjectConfiguration', ns):
include = proj_config.get('Include')
if include:
parts = include.split('|')
if len(parts) == 2:
configs.append((parts[0], parts[1]))
return configs
except Exception as e:
print(f"Warning: Could not parse project file: {e}")
return []
def print_available_configurations(project_file: Path) -> None:
"""Print all configurations available in the project"""
configs = get_available_configurations(project_file)
if not configs:
print("No configurations found or could not read project file.")
return
print("Available configurations in project:")
print("-" * 60)
from collections import defaultdict
grouped = defaultdict(list)
for config, platform in configs:
grouped[config].append(platform)
for config_name in sorted(grouped.keys()):
platforms = ', '.join(sorted(grouped[config_name]))
print(f" {config_name:25s} - {platforms}")
print(f"\nTotal: {len(configs)} configuration(s)")
print()
print("Configurations mapped in this build script:")
print("-" * 60)
for name, config in CONFIGS.items():
print(f" {name:15s} -> {config.config} / {config.platform}")
print()
_print_lock = threading.Lock()
def _build_configs_parallel(
msbuild_path: Path,
project_file: Path,
configs: 'List[BuildConfig]',
*,
rebuild: bool,
verbosity: str,
parallel: bool,
optimizations: bool,
project_dir: Path,
plugins_dir: Path,
toolset: str = 'v145',
) -> list:
"""Build all configurations simultaneously, printing output atomically."""
n = len(configs)
print(f"\nParallel-configs: 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'):
success, build_time, captured = build_configuration(
msbuild_path, project_file, config,
rebuild=rebuild, verbosity=verbosity,
parallel=parallel, optimizations=optimizations,
capture_output=True, toolset=toolset,
)
import io, contextlib
copy_buf = io.StringIO()
if success:
with contextlib.redirect_stdout(copy_buf):
copy_ok, file_size, dest_path = copy_output(project_dir, plugins_dir, config)
else:
copy_ok, file_size, dest_path = False, 0, None
with _print_lock:
all_ok = success and copy_ok
tag = "OK" if all_ok else "FAILED"
size_str = f"{file_size:,} bytes" if file_size > 0 else "N/A"
print(f"[{tag}] {config.name} ({format_time(build_time)}) {size_str}")
if dest_path:
print(f" -> {dest_path}")
if not all_ok:
copy_out = copy_buf.getvalue()
if copy_out.strip():
print(copy_out.rstrip())
if captured.strip():
print("--- Build output ---")
print(captured.rstrip())
with idx_lock:
results_by_idx[idx] = (config, success and copy_ok, build_time, file_size, dest_path)
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):
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.time() - total_start
print(f"\nAll {n} configs finished in {format_time(wall_time)} (wall clock)")
return [results_by_idx[i] for i in range(n)]
def main():
"""Main build function"""
parser = argparse.ArgumentParser(
description='Build nsis7z plugin (7-Zip ZS / mcmilk/7-Zip-zstd, Visual Studio 2026)',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s # Build all configurations
%(prog)s --list # List script configurations
%(prog)s --list-project # List all project configurations
%(prog)s --configs x86-unicode # Build only x86-unicode
%(prog)s --configs x86-unicode x64-unicode
%(prog)s --no-rebuild # Incremental build
%(prog)s --no-parallel # Single-threaded build
%(prog)s --no-clean # Skip cleanup
%(prog)s --verbosity minimal # Show more build output
"""
)
parser.add_argument(
'--configs', nargs='+',
choices=list(CONFIGS.keys()) + ['all'],
default=['all'],
help='Configurations to build (default: all)'
)
parser.add_argument('--rebuild', action='store_true', default=True)
parser.add_argument('--no-rebuild', action='store_false', dest='rebuild',
help='Incremental build (only changed files)')
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=True)
parser.add_argument('--no-clean', action='store_false', dest='clean')
parser.add_argument('--list', action='store_true',
help='List script configurations and exit')
parser.add_argument('--list-project', action='store_true',
help='List all configurations in the .vcxproj and exit')
parser.add_argument('--pause', action='store_true',
help='Wait for key press at the end')
parser.add_argument('--no-optimizations', action='store_true',
help='Disable additional build optimizations')
parser.add_argument('--vs-version', choices=['auto', '2026', '2022'], default='auto',
help='Visual Studio version to use (default: auto)')
args = parser.parse_args()
# Find MSBuild
msbuild_result = find_msbuild(args.vs_version)
if not msbuild_result:
print("ERROR: MSBuild not found!")
return 1
msbuild_path, platform_toolset, vs_version_name = msbuild_result
# Get project paths
project_dir, project_file, plugins_dir = get_project_paths(platform_toolset)
if args.list_project:
if not project_file.exists():
print(f"ERROR: Project file not found: {project_file}")
return 1
print_available_configurations(project_file)
return 0
if args.list:
print("Build script configurations (7-Zip ZS / mcmilk, VS2026):")
print("-" * 60)
for name, config in CONFIGS.items():
print(f" {name:15s} -> {config.config} / {config.platform}")
print(f"\nTotal: {len(CONFIGS)} configuration(s)")
return 0
if not project_file.exists():
print(f"ERROR: Project file not found: {project_file}")
print()
print("Make sure the submodule is initialised:")
print(" git submodule update --init versions/7-zip-zstd")
return 1
# Determine configurations to build
if 'all' in args.configs:
configs_to_build = list(CONFIGS.values())
else:
configs_to_build = [CONFIGS[name] for name in args.configs]
# Print header
print("=" * 60)
print(
f"Building nsis7z plugin (7-Zip ZS, VS2026)"
f" - {len(configs_to_build)} configuration(s)"
)
print("=" * 60)
print(f"Visual Studio: {vs_version_name} (toolset {platform_toolset})")
print(f"MSBuild: {msbuild_path}")
print(f"Project: {project_file}")
print(f"Plugins: {plugins_dir}")
print(f"Rebuild: {args.rebuild}")
print(f"Verbosity: {args.verbosity}")
print()
use_optimizations = not args.no_optimizations
print_cpu_info(args.parallel, use_optimizations)
# Build
build_results = []
total_start_time = time.time()
if args.parallel and len(configs_to_build) > 1:
build_results = _build_configs_parallel(
msbuild_path, project_file, configs_to_build,
rebuild=args.rebuild, verbosity=args.verbosity,
parallel=args.parallel, optimizations=use_optimizations,
project_dir=project_dir, plugins_dir=plugins_dir,
toolset=platform_toolset,
)
else:
for i, config in enumerate(configs_to_build, 1):
success, build_time, _ = build_configuration(
msbuild_path, project_file, config,
rebuild=args.rebuild, verbosity=args.verbosity,
parallel=args.parallel, optimizations=use_optimizations,
counter=f"{i}/{len(configs_to_build)}",
toolset=platform_toolset,
)
if not success:
print(f"ERROR: {config.name} build failed! (Time: {format_time(build_time)})")
build_results.append((config, False, build_time, 0, None))
continue
success, file_size, dest_path = copy_output(project_dir, plugins_dir, config)
print(f"Build time: {format_time(build_time)}")
build_results.append((config, success, build_time, file_size, dest_path))
total_time = time.time() - total_start_time
# Summary
print("\n" + "=" * 50)
all_success = all(success for _, success, _, _, _ in build_results)
if all_success:
print("ALL BUILDS SUCCESSFUL!")
print("=" * 50)
print("\nPlugins copied to:")
for config, _, build_time, file_size, dest_path in build_results:
dest = dest_path or plugins_dir / config.dest_dir / 'nsis7z.dll'
print(f" - {dest}")
if args.clean:
print()
clean_build_artifacts(project_dir, configs_to_build)
else:
print("SOME BUILDS FAILED!")
print("=" * 50)
print("\nFailed configurations:")
for config, success, build_time, file_size, dest_path in build_results:
if not success:
print(f" - {config.name}")
print("\n" + "-" * 50)
print("Build Summary:")
for config, success, build_time, file_size, dest_path in build_results:
status = "OK" if success else "FAIL"
size_str = f"{file_size:,} bytes" if file_size > 0 else "N/A"
print(f" {status} {config.name:15s} - {format_time(build_time):8s} - {size_str}")
print("-" * 50)
print(f"Total time: {format_time(total_time)}")
print()
if args.pause:
try:
input("Press Enter to continue...")
except EOFError:
pass
return 0 if all_success else 1
if __name__ == '__main__':
sys.exit(main())
Submodule versions/7-zip-zstd added at 6146959af0
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
// StdAfx.h
#ifndef __STDAFX_H
#define __STDAFX_H
#include "Common/Common.h"
#include <windows.h>
#include <commctrl.h>
#include "Common/MyWindows.h"
#include "Common/NewHandler.h"
#include "pluginapi.h"
#endif
@@ -0,0 +1,3 @@
// StdAfx.cpp
#include "StdAfx.h"
@@ -0,0 +1 @@
#include <winresrc.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_ */
@@ -0,0 +1,190 @@
#include "StdAfx.h"
#include "../../UI/NSIS/ExtractCallbackConsole.h"
#include "Common/StringConvert.h"
#pragma warning(disable: 4100)
#define IDC_PROGRESS 1004
#define IDC_INTROTEXT 1006
#define EXTRACTFUNC(funcname) extern "C" { \
void __declspec(dllexport) __cdecl funcname(HWND hwndParent, int string_size, \
TCHAR *variables, stack_t **stacktop, \
extra_parameters *extra) \
{ \
EXDLL_INIT();\
g_lastVal = -1; \
g_hwndParent=hwndParent; \
HWND hwndDlg = FindWindowEx(g_hwndParent, NULL, TEXT("#32770"), NULL); \
g_hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESS); \
g_hwndText = GetDlgItem(hwndDlg, IDC_INTROTEXT); \
TCHAR sArchive[1024], *outDir = getuservariable(INST_OUTDIR); \
popstring(sArchive); \
g_pluginExtra = extra; \
#define EXTRACTFUNCEND } }
HINSTANCE g_hInstance2;
HWND g_hwndParent;
HWND g_hwndProgress;
HWND g_hwndText;
extra_parameters *g_pluginExtra;
void DoInitialize();
int DoExtract(LPTSTR archive, LPTSTR dir, bool overwrite, bool expath, ExtractProgressHandler epc, const UStringVector& skipPatterns);
int DoExtractWithFile(LPTSTR archive, LPTSTR dir, bool overwrite, bool expath, ExtractProgressWithFileHandler epc, const UStringVector& skipPatterns);
int g_progressCallback = -1;
int g_lastVal = -1;
TCHAR* g_sDetails;
static void PopSkipPatterns(UStringVector& skipPatterns)
{
TCHAR *buf = new TCHAR[g_stringsize];
while (popstring(buf) == 0 && lstrlen(buf) > 0)
{
#ifdef UNICODE
skipPatterns.Add(UString(buf));
#else
skipPatterns.Add(MultiByteToUnicodeString(AString(buf)));
#endif
}
delete[] buf;
}
int GetPercentComplete(UInt64 completedSize, UInt64 totalSize)
{
if (totalSize == 0) return 0;
const int nsisProgressMax = 30000;
int val = (int)((completedSize*nsisProgressMax)/totalSize);
if (val < 0) return 0;
if (val > nsisProgressMax) return nsisProgressMax;
return val;
}
void SimpleProgressHandler(UInt64 completedSize, UInt64 totalSize)
{
int val = GetPercentComplete(completedSize, totalSize);
if (g_lastVal != val)
SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}
void CallbackProgressHandler(UInt64 completedSize, UInt64 totalSize)
{
int val = 0;
if (totalSize > 0)
{
val = GetPercentComplete(completedSize, totalSize);
static TCHAR buf[32];
wsprintf(buf, TEXT("%lu"), totalSize);
pushstring(buf);
wsprintf(buf, TEXT("%lu"), completedSize);
pushstring(buf);
g_pluginExtra->ExecuteCodeSegment(g_progressCallback-1, 0);
}
if (g_lastVal != val)
SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}
void CallbackFileProgressHandler(UInt64 completedSize, UInt64 totalSize, const wchar_t *fileName)
{
int val = 0;
if (totalSize > 0)
val = GetPercentComplete(completedSize, totalSize);
// Notify NSIS only when a new file starts (filename changes)
if (fileName && fileName[0])
{
static TCHAR fileNameBuf[MAX_PATH];
static TCHAR prevFileNameBuf[MAX_PATH];
static TCHAR buf[32];
lstrcpyn(fileNameBuf, (const TCHAR*)fileName, MAX_PATH);
if (lstrcmp(fileNameBuf, prevFileNameBuf) != 0)
{
lstrcpy(prevFileNameBuf, fileNameBuf);
pushstring(fileNameBuf);
wsprintf(buf, TEXT("%lu"), (DWORD)totalSize);
pushstring(buf);
wsprintf(buf, TEXT("%lu"), (DWORD)completedSize);
pushstring(buf);
g_pluginExtra->ExecuteCodeSegment(g_progressCallback-1, 0);
}
}
if (g_lastVal != val)
SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}
void DetailsProgressHandler(UInt64 completedSize, UInt64 totalSize)
{
int val = 0;
if (totalSize > 0)
{
val = GetPercentComplete(completedSize, totalSize);
TCHAR* buf = new TCHAR[g_stringsize];
TCHAR* buf2 = new TCHAR[g_stringsize];
wsprintf(buf, TEXT("%d%% (%d / %d MB)"), (int)(val?val/300:0), (int)(completedSize?completedSize/(1024*1024):0), (int)(totalSize/(1024*1024)));
wsprintf(buf2, g_sDetails, buf);
SetWindowText(g_hwndText, buf2);
delete[] buf;
delete[] buf2;
}
if (g_lastVal != val)
SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}
EXTRACTFUNC(Extract)
{
UStringVector skipPatterns;
PopSkipPatterns(skipPatterns);
DoExtract(sArchive, outDir, true, true, (ExtractProgressHandler)SimpleProgressHandler, skipPatterns);
}
EXTRACTFUNCEND
EXTRACTFUNC(ExtractWithDetails)
{
g_sDetails = new TCHAR[string_size];
popstring(g_sDetails);
UStringVector skipPatterns;
PopSkipPatterns(skipPatterns);
DoExtract(sArchive, outDir, true, true, (ExtractProgressHandler)DetailsProgressHandler, skipPatterns);
delete[] g_sDetails;
}
EXTRACTFUNCEND
EXTRACTFUNC(ExtractWithCallback)
{
g_progressCallback = popint();
UStringVector skipPatterns;
PopSkipPatterns(skipPatterns);
DoExtract(sArchive, outDir, true, true, (ExtractProgressHandler)CallbackProgressHandler, skipPatterns);
}
EXTRACTFUNCEND
EXTRACTFUNC(ExtractWithFileCallback)
{
g_progressCallback = popint();
UStringVector skipPatterns;
PopSkipPatterns(skipPatterns);
DoExtractWithFile(sArchive, outDir, true, true, CallbackFileProgressHandler, skipPatterns);
}
EXTRACTFUNCEND
extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
g_hInstance2=(HINSTANCE)hInst;
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
DoInitialize();
}
return TRUE;
}
@@ -0,0 +1,229 @@
/*
* 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
# define _vstprintf _vswprintf
#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
#define _tcstok wcstok
// 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
#define _tenviron environ
#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
#define _totupper toupper
#define _totlower tolower
// 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)
#define _istgraph isgraph
#define _istascii __isascii
#define __TFILE__ _T(__FILE__)
#define __TDATE__ _T(__DATE__)
#define __TTIME__ _T(__TIME__)
@@ -0,0 +1,292 @@
#include "StdAfx.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) lstrcpy(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);
}
@@ -0,0 +1,115 @@
#ifndef ___NSIS_PLUGIN__H___
#define ___NSIS_PLUGIN__H___
#ifdef __cplusplus
extern "C" {
#endif
#include "api.h"
#ifdef _UNICODE
#ifndef _T
#define __T(x) L ## x
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
#endif
#else
#ifndef _T
#define _T(x) x
#define _TEXT(x) x
#endif
#endif
#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___
@@ -0,0 +1,19 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by resource.rc
//
#define MY_VER_BUILD 0
#define DBG_FL 0
#define MY_VER_MAJOR 9
#define MY_VER_MINOR 20
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
@@ -0,0 +1,99 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 25,01,0,0
PRODUCTVERSION 25,01,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Igor Pavlov"
VALUE "FileDescription", "7-Zip NSIS Plug-in"
VALUE "FileVersion", "25.01.0.0"
VALUE "InternalName", "nsis7z"
VALUE "LegalCopyright", "Copyright (c) 1999-2025 Igor Pavlov, Nik Medved, Marek Mizanin, Stuart Welch"
VALUE "OriginalFilename", "nsis7z.dll"
VALUE "ProductName", "7-Zip"
VALUE "ProductVersion", "25.01.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
@@ -0,0 +1,163 @@
// ExtractCallbackConsole.h
#include "StdAfx.h"
#include "Common/Common.h"
#include "ExtractCallbackConsole.h"
#include "UserInputUtils2.h"
#include "NSISBreak.h"
#include "Common/Wildcard.h"
#include "Windows/FileDir.h"
#include "Windows/FileFind.h"
#include "Windows/TimeUtils.h"
#include "Windows/Defs.h"
#include "Windows/PropVariant.h"
#include "Windows/ErrorMsg.h"
#include "Windows/PropVariantConv.h"
#include "7zip/Common/FilePathAutoRename.h"
#include "7zip/UI/Common/ExtractingFilePath.h"
using namespace NWindows;
using namespace NFile;
using namespace NDir;
extern HWND g_hwndProgress;
void CExtractCallbackConsole::UpdateProgress()
{
// Prima controlla se c'è il callback con filename (nuova funzione)
if (ProgressWithFileHandler != NULL)
{
if (completedSize != -1 || totalSize > 0)
ProgressWithFileHandler(completedSize, totalSize, CurrentFileName.Ptr());
else
ProgressWithFileHandler(0, 0, CurrentFileName.Ptr());
}
// Altrimenti usa il callback originale (per compatibilitĂ )
else if (ProgressHandler != NULL)
{
if (completedSize != -1 || totalSize > 0)
ProgressHandler(completedSize, totalSize);
else
ProgressHandler(0, 0);
}
}
STDMETHODIMP CExtractCallbackConsole::SetTotal(UInt64 val)
{
totalSize = val;
UpdateProgress();
if (NNSISBreak::TestBreakSignal())
return E_ABORT;
return S_OK;
}
STDMETHODIMP CExtractCallbackConsole::SetCompleted(const UInt64 *val)
{
completedSize = *val;
UpdateProgress();
if (NNSISBreak::TestBreakSignal())
return E_ABORT;
return S_OK;
}
STDMETHODIMP CExtractCallbackConsole::AskOverwrite(
const wchar_t *existName, const FILETIME *, const UInt64 *,
const wchar_t *newName, const FILETIME *, const UInt64 *,
Int32 *answer)
{
*answer = NOverwriteAnswer::kYesToAll;
return S_OK;
}
STDMETHODIMP CExtractCallbackConsole::PrepareOperation(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 *position)
{
// Memorizza il nome del file corrente per passarlo al callback
CurrentFileName = name;
return S_OK;
}
STDMETHODIMP CExtractCallbackConsole::MessageError(const wchar_t *message)
{
NumFileErrorsInCurrentArchive++;
NumFileErrors++;
return S_OK;
}
STDMETHODIMP CExtractCallbackConsole::SetOperationResult(Int32 opRes, Int32 encrypted)
{
switch(opRes)
{
case NArchive::NExtract::NOperationResult::kOK:
break;
default:
{
NumFileErrorsInCurrentArchive++;
NumFileErrors++;
}
}
return S_OK;
}
#ifndef Z7_NO_CRYPTO
HRESULT CExtractCallbackConsole::SetPassword(const UString &password)
{
PasswordIsDefined = true;
Password = password;
return S_OK;
}
STDMETHODIMP CExtractCallbackConsole::CryptoGetTextPassword(BSTR *password)
{
if (!PasswordIsDefined)
{
Password = GetPassword(OutStream);
PasswordIsDefined = true;
}
return StringToBstr(Password, password);
}
#endif
HRESULT CExtractCallbackConsole::BeforeOpen(const wchar_t *name, bool testMode)
{
NumArchives++;
NumFileErrorsInCurrentArchive = 0;
return S_OK;
}
HRESULT CExtractCallbackConsole::OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
{
if (result != S_OK)
{
NumArchiveErrors++;
}
return S_OK;
}
HRESULT CExtractCallbackConsole::ThereAreNoFiles()
{
return S_OK;
}
HRESULT CExtractCallbackConsole::ExtractResult(HRESULT result)
{
if (result == S_OK)
{
if (NumFileErrorsInCurrentArchive != 0)
{
NumArchiveErrors++;
}
return result;
}
NumArchiveErrors++;
if (result == E_ABORT || result == ERROR_DISK_FULL)
return result;
return S_OK;
}
@@ -0,0 +1,98 @@
// ExtractCallbackConsole.h
// NSIS version adapted for 7-Zip 25.01 API
#ifndef __EXTRACTCALLBACKCONSOLE_H
#define __EXTRACTCALLBACKCONSOLE_H
#include "Common/MyCom.h"
#include "Common/MyString.h"
#include "Common/StdOutStream.h"
#include "7zip/Common/FileStreams.h"
#include "7zip/IPassword.h"
#include "7zip/Archive/IArchive.h"
#include "7zip/UI/Common/ArchiveExtractCallback.h"
#include "7zip/UI/Console/OpenCallbackConsole.h"
typedef void (*ExtractProgressHandler)(UInt64 completedSize, UInt64 totalSize);
typedef void (*ExtractProgressWithFileHandler)(UInt64 completedSize, UInt64 totalSize, const wchar_t *fileName);
class CExtractCallbackConsole Z7_final:
public IFolderArchiveExtractCallback,
public IExtractCallbackUI,
#ifndef Z7_NO_CRYPTO
public ICryptoGetTextPassword,
#endif
public COpenCallbackConsole,
public CMyUnknownImp
{
Z7_COM_QI_BEGIN2(IFolderArchiveExtractCallback)
#ifndef Z7_NO_CRYPTO
Z7_COM_QI_ENTRY(ICryptoGetTextPassword)
#endif
Z7_COM_QI_END
Z7_COM_ADDREF_RELEASE
// IProgress
Z7_IFACE_COM7_IMP(IProgress)
// IFolderArchiveExtractCallback
Z7_IFACE_COM7_IMP(IFolderArchiveExtractCallback)
// IExtractCallbackUI (non-COM interface)
Z7_IFACE_IMP(IExtractCallbackUI)
#ifndef Z7_NO_CRYPTO
Z7_IFACE_COM7_IMP(ICryptoGetTextPassword)
#endif
public:
#ifndef Z7_NO_CRYPTO
bool PasswordIsDefined;
UString Password;
#endif
UInt64 NumArchives;
UInt64 NumArchiveErrors;
UInt64 NumFileErrors;
UInt64 NumFileErrorsInCurrentArchive;
CStdOutStream *OutStream;
UInt64 totalSize, completedSize, lastVal;
ExtractProgressHandler ProgressHandler;
ExtractProgressWithFileHandler ProgressWithFileHandler;
UString CurrentFileName;
CExtractCallbackConsole():
PasswordIsDefined(false),
NumArchives(0),
NumArchiveErrors(0),
NumFileErrors(0),
NumFileErrorsInCurrentArchive(0),
OutStream(NULL),
totalSize((UInt64)(Int64)-1),
completedSize(0),
lastVal(0),
ProgressHandler(NULL),
ProgressWithFileHandler(NULL)
{}
void Init()
{
NumArchives = 0;
NumArchiveErrors = 0;
NumFileErrors = 0;
NumFileErrorsInCurrentArchive = 0;
totalSize = (UInt64)(Int64)-1;
completedSize = 0;
lastVal = 0;
ProgressHandler = NULL;
ProgressWithFileHandler = NULL;
}
void UpdateProgress();
};
#endif
+313
View File
@@ -0,0 +1,313 @@
// Main.cpp
#include "StdAfx.h"
#include "Common/Common.h"
#include "Common/MyInitGuid.h"
#include "Common/CommandLineParser.h"
#include "Common/MyException.h"
#include "Common/IntToString.h"
#include "Common/StdOutStream.h"
#include "Common/StringConvert.h"
#include "Common/StringToInt.h"
#include "Windows/FileDir.h"
#include "Windows/FileName.h"
#include "Windows/Defs.h"
#include "Windows/ErrorMsg.h"
#ifdef _WIN32
#include "Windows/MemoryLock.h"
#endif
#include "7zip/IPassword.h"
#include "7zip/ICoder.h"
#include "7zip/UI/Common/UpdateAction.h"
#include "7zip/UI/Common/Update.h"
#include "7zip/UI/Common/Extract.h"
#include "7zip/UI/Common/ArchiveCommandLine.h"
#include "7zip/UI/Common/ExitCode.h"
#ifdef EXTERNAL_CODECS
#include "7zip/UI/Common/LoadCodecs.h"
#endif
//#include "../../Compress/LZMA_Alone/LzmaBenchCon.h"
#include "7zip/UI/Console/OpenCallbackConsole.h"
#include "ExtractCallbackConsole.h"
#include "7zip/MyVersion.h"
#if defined( _WIN32) && defined( _7ZIP_LARGE_PAGES)
extern "C"
{
#include "C/Alloc.h"
}
#endif
using namespace NWindows;
using namespace NFile;
using namespace NCommandLineParser;
HINSTANCE g_hInstance = 0;
// ---------------------------
// exception messages
#ifndef _WIN32
static void GetArguments(int numArguments, const char *arguments[], UStringVector &parts)
{
parts.Clear();
for(int i = 0; i < numArguments; i++)
{
UString s = MultiByteToUnicodeString(arguments[i]);
parts.Add(s);
}
}
#endif
#ifdef EXTERNAL_CODECS
static void PrintString(CStdOutStream &stdStream, const AString &s, int size)
{
int len = s.Length();
stdStream << s;
for (int i = len; i < size; i++)
stdStream << ' ';
}
#endif
static inline char GetHex(Byte value)
{
return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
}
#ifdef _WIN32
void SwitchFileAPIEncoding(BOOL ansi)
{
if (ansi)
{
SetFileApisToOEM();
}
else
{
SetFileApisToANSI();
}
}
#endif
int DoExtractArchive(UString archive, UString targetDir, bool overwrite, bool extractPaths, ExtractProgressHandler epc, const UStringVector& skipPatterns)
{
CCodecs *codecs = new CCodecs;
CMyComPtr<IUnknown> compressCodecsInfo = codecs;
HRESULT result = codecs->Load();
if (result != S_OK)
throw CSystemException(result);
if (codecs->Formats.Size() == 0) throw -1;
CIntVector formatIndices;
if (!codecs->FindFormatForArchiveType(L"7z", formatIndices))
{
throw -1;
}
BOOL bApisAreAnsi = AreFileApisANSI();
#ifdef _WIN32
if (bApisAreAnsi)
SwitchFileAPIEncoding(bApisAreAnsi);
#endif
CExtractCallbackConsole *ecs = new CExtractCallbackConsole();
CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs;
ecs->Init();
ecs->ProgressHandler = epc; // Imposta DOPO Init()
COpenCallbackConsole openCallback;
CExtractOptions eo;
eo.StdOutMode = false;
eo.PathMode = extractPaths?NExtract::NPathMode::kCurPaths:NExtract::NPathMode::kNoPaths;
eo.TestMode = false;
eo.OverwriteMode = overwrite?NExtract::NOverwriteMode::kOverwrite:NExtract::NOverwriteMode::kSkip;
eo.OutputDir = targetDir;
eo.YesToAll = true;
#ifdef COMPRESS_MT
CObjectVector<CProperty> prp;
eo.Properties = prp;
#endif
UString errorMessage;
CDecompressStat stat;
NWildcard::CCensor wildcardCensor;
NWildcard::CCensorPathProps props;
props.Recursive = true;
props.WildcardMatching = true;
wildcardCensor.AddItem(NWildcard::ECensorPathMode::k_FullPath, true, L"*", props);
for (unsigned i = 0; i < skipPatterns.Size(); i++)
{
const UString &p = skipPatterns[i];
if (p.IsEmpty()) continue;
// Keep only single-name patterns: multi-segment ones land in a separate Pair
// and would be silently ignored by Pairs.Front().Head below.
if (p.Find(L'\\') >= 0 || p.Find(L'/') >= 0) continue;
wildcardCensor.AddItem(NWildcard::ECensorPathMode::k_FullPath, false, p, props);
}
UStringVector ArchivePathsSorted;
UStringVector ArchivePathsFullSorted;
ArchivePathsSorted.Add(archive);
UString fullPath;
NFile::NDir::MyGetFullPathName(archive, fullPath);
ArchivePathsFullSorted.Add(fullPath);
UStringVector v1, v2;
v1.Add(fs2us(archive));
v2.Add(fs2us(archive));
const NWildcard::CCensorNode &wildcardCensorHead =
wildcardCensor.Pairs.Front().Head;
result = Extract(
codecs, CObjectVector<COpenType>(), CIntVector(),
v1, v2,
wildcardCensorHead,
eo, ecs, ecs, ecs,
#ifndef Z7_SFX
NULL, // hash
#endif
errorMessage, stat);
#ifdef _WIN32
if (bApisAreAnsi)
SwitchFileAPIEncoding(!bApisAreAnsi);
#endif
if (!errorMessage.IsEmpty())
{
if (result == S_OK)
result = E_FAIL;
}
if (ecs->NumArchiveErrors != 0 || ecs->NumFileErrors != 0)
{
if (result != S_OK)
throw CSystemException(result);
return NExitCode::kFatalError;
}
if (result != S_OK)
throw CSystemException(result);
return 0;
}
int DoExtractArchiveWithFile(UString archive, UString targetDir, bool overwrite, bool extractPaths, ExtractProgressWithFileHandler epc, const UStringVector& skipPatterns)
{
CCodecs *codecs = new CCodecs;
CMyComPtr<IUnknown> compressCodecsInfo = codecs;
HRESULT result = codecs->Load();
if (result != S_OK)
throw CSystemException(result);
if (codecs->Formats.Size() == 0) throw -1;
CIntVector formatIndices;
if (!codecs->FindFormatForArchiveType(L"7z", formatIndices))
{
throw -1;
}
BOOL bApisAreAnsi = AreFileApisANSI();
#ifdef _WIN32
if (bApisAreAnsi)
SwitchFileAPIEncoding(bApisAreAnsi);
#endif
CExtractCallbackConsole *ecs = new CExtractCallbackConsole();
CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs;
ecs->Init();
ecs->ProgressWithFileHandler = epc; // Imposta DOPO Init() per evitare che venga resettato
COpenCallbackConsole openCallback;
CExtractOptions eo;
eo.StdOutMode = false;
eo.PathMode = extractPaths?NExtract::NPathMode::kCurPaths:NExtract::NPathMode::kNoPaths;
eo.TestMode = false;
eo.OverwriteMode = overwrite?NExtract::NOverwriteMode::kOverwrite:NExtract::NOverwriteMode::kSkip;
eo.OutputDir = targetDir;
eo.YesToAll = true;
#ifdef COMPRESS_MT
CObjectVector<CProperty> prp;
eo.Properties = prp;
#endif
UString errorMessage;
CDecompressStat stat;
NWildcard::CCensor wildcardCensor;
NWildcard::CCensorPathProps props;
props.Recursive = true;
props.WildcardMatching = true;
wildcardCensor.AddItem(NWildcard::ECensorPathMode::k_FullPath, true, L"*", props);
for (unsigned i = 0; i < skipPatterns.Size(); i++)
{
const UString &p = skipPatterns[i];
if (p.IsEmpty()) continue;
// Keep only single-name patterns: multi-segment ones land in a separate Pair
// and would be silently ignored by Pairs.Front().Head below.
if (p.Find(L'\\') >= 0 || p.Find(L'/') >= 0) continue;
wildcardCensor.AddItem(NWildcard::ECensorPathMode::k_FullPath, false, p, props);
}
UStringVector ArchivePathsSorted;
UStringVector ArchivePathsFullSorted;
ArchivePathsSorted.Add(archive);
UString fullPath;
NFile::NDir::MyGetFullPathName(archive, fullPath);
ArchivePathsFullSorted.Add(fullPath);
UStringVector v1, v2;
v1.Add(fs2us(archive));
v2.Add(fs2us(archive));
const NWildcard::CCensorNode &wildcardCensorHead =
wildcardCensor.Pairs.Front().Head;
result = Extract(
codecs, CObjectVector<COpenType>(), CIntVector(),
v1, v2,
wildcardCensorHead,
eo, ecs, ecs, ecs,
#ifndef Z7_SFX
NULL, // hash
#endif
errorMessage, stat);
#ifdef _WIN32
if (bApisAreAnsi)
SwitchFileAPIEncoding(!bApisAreAnsi);
#endif
if (!errorMessage.IsEmpty())
{
if (result == S_OK)
result = E_FAIL;
}
if (ecs->NumArchiveErrors != 0 || ecs->NumFileErrors != 0)
{
if (result != S_OK)
throw CSystemException(result);
return NExitCode::kFatalError;
}
if (result != S_OK)
throw CSystemException(result);
return 0;
}
+109
View File
@@ -0,0 +1,109 @@
// MainAr.cpp
#include "StdAfx.h"
// #include <locale.h>
#include "Common/Common.h"
#include "Windows/ErrorMsg.h"
#include "Common/StdOutStream.h"
#include "Common/NewHandler.h"
#include "Common/MyException.h"
#include "Common/StringConvert.h"
#include "7zip/UI/Common/ExitCode.h"
#include "7zip/UI/Common/ArchiveCommandLine.h"
#include "ExtractCallbackConsole.h"
#include "NSISBreak.h"
using namespace NWindows;
#ifdef _WIN32
#pragma warning(disable : 4996)
#ifndef _UNICODE
bool g_IsNT = false;
#endif
#if !defined(_UNICODE) || !defined(_WIN64)
static inline bool IsItWindowsNT()
{
OSVERSIONINFO versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
if (!::GetVersionEx(&versionInfo))
return false;
return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
}
#endif
#endif
void DoInitialize()
{
#ifdef _WIN32
#ifdef _UNICODE
#ifndef _WIN64
if (!IsItWindowsNT())
{
//(*g_StdStream) << "This program requires Windows NT/2000/2003/2008/XP/Vista";
// return NExitCode::kFatalError;
return;
}
#endif
#else
g_IsNT = IsItWindowsNT();
#endif
#endif
}
extern int DoExtractArchive(UString archive, UString targetDir, bool overwrite, bool extractPathes, ExtractProgressHandler epc, const UStringVector& skipPatterns);
extern int DoExtractArchiveWithFile(UString archive, UString targetDir, bool overwrite, bool extractPathes, ExtractProgressWithFileHandler epc, const UStringVector& skipPatterns);
int DoExtract(LPTSTR archive, LPTSTR dir, bool overwrite, bool expath, ExtractProgressHandler epc, const UStringVector& skipPatterns)
{
if (!archive || !dir) return -1;
int res = 0;
try
{
#ifdef UNICODE
UString uarchive(archive);
UString udir(dir);
#else
UString uarchive = MultiByteToUnicodeString(AString(archive));
UString udir = MultiByteToUnicodeString(AString(dir));
#endif
res = DoExtractArchive(uarchive, udir, overwrite, expath, epc, skipPatterns);
}
catch(...)
{
return -1;
}
return res;
}
int DoExtractWithFile(LPTSTR archive, LPTSTR dir, bool overwrite, bool expath, ExtractProgressWithFileHandler epc, const UStringVector& skipPatterns)
{
if (!archive || !dir) return -1;
int res = 0;
try
{
#ifdef UNICODE
UString uarchive(archive);
UString udir(dir);
#else
UString uarchive = MultiByteToUnicodeString(AString(archive));
UString udir = MultiByteToUnicodeString(AString(dir));
#endif
res = DoExtractArchiveWithFile(uarchive, udir, overwrite, expath, epc, skipPatterns);
}
catch(...)
{
return -1;
}
return res;
}
@@ -0,0 +1,31 @@
// ConsoleClose.cpp
#include "StdAfx.h"
#include "NSISBreak.h"
static int g_BreakCounter = 0;
namespace NNSISBreak {
void SendBreakSignal()
{
g_BreakCounter++;
}
bool TestBreakSignal()
{
/*
if (g_BreakCounter > 0)
return true;
*/
return (g_BreakCounter > 0);
}
void CheckCtrlBreak()
{
if (TestBreakSignal())
throw CCtrlBreakException();
}
}
@@ -0,0 +1,16 @@
#ifndef __NSISBREAKUTILS_H
#define __NSISBREAKUTILS_H
namespace NNSISBreak {
bool TestBreakSignal();
void SendBreakSignal();
class CCtrlBreakException
{};
void CheckCtrlBreak();
}
#endif
+9
View File
@@ -0,0 +1,9 @@
// StdAfx.h
#ifndef __STDAFX_H
#define __STDAFX_H
#include <Windows.h>
#include "7zip/Bundles/Nsis7z/pluginapi.h"
#endif
@@ -0,0 +1,60 @@
// UserInputUtils.cpp
#include "StdAfx.h"
#include "Common/StdInStream.h"
#include "Common/StringConvert.h"
#include "UserInputUtils2.h"
static const char kYes = 'Y';
static const char kNo = 'N';
static const char kYesAll = 'A';
static const char kNoAll = 'S';
static const char kAutoRename = 'U';
static const char kQuit = 'Q';
static const char *kFirstQuestionMessage = "?\n";
static const char *kHelpQuestionMessage =
"(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename / (Q)uit? ";
// return true if pressed Quite;
// in: anAll
// out: anAll, anYes;
NUserAnswerMode::EEnum ScanUserYesNoAllQuit2(CStdOutStream *outStream)
{
(*outStream) << kFirstQuestionMessage;
for(;;)
{
(*outStream) << kHelpQuestionMessage;
AString scannedString;
g_StdIn.ScanAStringUntilNewLine(scannedString);
scannedString.Trim();
if(!scannedString.IsEmpty())
switch(::MyCharUpper(scannedString[0]))
{
case kYes:
return NUserAnswerMode::kYes;
case kNo:
return NUserAnswerMode::kNo;
case kYesAll:
return NUserAnswerMode::kYesAll;
case kNoAll:
return NUserAnswerMode::kNoAll;
case kAutoRename:
return NUserAnswerMode::kAutoRename;
case kQuit:
return NUserAnswerMode::kQuit;
}
}
}
UString GetPassword(CStdOutStream *outStream)
{
(*outStream) << "\nEnter password:";
outStream->Flush();
AString oemPassword;
g_StdIn.ScanAStringUntilNewLine(oemPassword);
return MultiByteToUnicodeString(oemPassword, CP_OEMCP);
}
@@ -0,0 +1,24 @@
// UserInputUtils.h
#ifndef __USERINPUTUTILS_H
#define __USERINPUTUTILS_H
#include "Common/StdOutStream.h"
namespace NUserAnswerMode {
enum EEnum
{
kYes,
kNo,
kYesAll,
kNoAll,
kAutoRename,
kQuit
};
}
NUserAnswerMode::EEnum ScanUserYesNoAllQuit2(CStdOutStream *outStream);
UString GetPassword(CStdOutStream *outStream);
#endif